[UI] Refactor Messages fragment.

This commit is contained in:
Kuba Szczodrzyński 2020-04-04 21:31:14 +02:00
parent c0aeb0d2f3
commit f98b174857
56 changed files with 832 additions and 669 deletions

View File

@ -816,7 +816,7 @@ fun View.findParentById(targetId: Int): View? {
return null
}
fun CoroutineScope.startCoroutineTimer(delayMillis: Long = 0, repeatMillis: Long = 0, action: () -> Unit) = launch {
fun CoroutineScope.startCoroutineTimer(delayMillis: Long = 0, repeatMillis: Long = 0, action: suspend CoroutineScope.() -> Unit) = launch {
delay(delayMillis)
if (repeatMillis > 0) {
while (true) {

View File

@ -68,9 +68,9 @@ import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment
import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity
import pl.szczodrzynski.edziennik.ui.modules.messages.MessageFragment
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeFragment
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesListFragment
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesListFragmentOld
import pl.szczodrzynski.edziennik.ui.modules.messages.compose.MessagesComposeFragment
import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsListFragment
import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsNewFragment
@ -889,9 +889,9 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
}
app.profileLoad(id) {
MessagesFragment.pageSelection = -1
MessagesListFragment.tapPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
MessagesListFragment.topPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
MessagesListFragment.bottomPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
MessagesListFragmentOld.tapPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
MessagesListFragmentOld.topPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
MessagesListFragmentOld.bottomPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
setDrawerItems()
// the drawer profile is updated automatically when the drawer item is clicked

View File

@ -13,8 +13,8 @@ import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOME
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT
internal const val FEATURE_TIMETABLE = 1
internal const val FEATURE_AGENDA = 2

View File

@ -11,8 +11,8 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNIK_API_MESSAGES_INBOX
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikApi
import pl.szczodrzynski.edziennik.data.db.entity.*
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_DELETED
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_DELETED
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.getBoolean
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.Utils.crc32
@ -33,11 +33,11 @@ class IdziennikApiMessagesInbox(override val data: DataIdziennik,
return@apiGet
}
json.asJsonObjectList()?.forEach { jMessage ->
val subject = jMessage.getString("tytul")
if (subject?.contains("(") == true && subject.startsWith("iDziennik - "))
json.asJsonObjectList().forEach { jMessage ->
val subject = jMessage.getString("tytul") ?: ""
if (subject.contains("(") && subject.startsWith("iDziennik - "))
return@forEach
if (subject?.startsWith("Uwaga dla ucznia (klasa:") == true)
if (subject.startsWith("Uwaga dla ucznia (klasa:"))
return@forEach
val messageIdStr = jMessage.getString("id")
@ -64,13 +64,12 @@ class IdziennikApiMessagesInbox(override val data: DataIdziennik,
rTeacher.setTeacherType(Teacher.TYPE_OTHER)
val message = Message(
profileId,
messageId,
subject,
body,
if (jMessage.getBoolean("rekordUsuniety") == true) TYPE_DELETED else TYPE_RECEIVED,
rTeacher.id,
-1
profileId = profileId,
id = messageId,
type = if (jMessage.getBoolean("rekordUsuniety") == true) TYPE_DELETED else TYPE_RECEIVED,
subject = subject,
body = body,
senderId = rTeacher.id
)
val messageRecipient = MessageRecipient(
@ -81,7 +80,7 @@ class IdziennikApiMessagesInbox(override val data: DataIdziennik,
/*messageId*/ messageId
)
data.messageIgnoreList.add(message)
data.messageList.add(message)
data.messageRecipientList.add(messageRecipient)
data.setSeenMetadataList.add(Metadata(
profileId,

View File

@ -13,7 +13,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNIK_API_MESSAGES_SENT
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikApi
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.utils.Utils.crc32
@ -46,13 +46,12 @@ class IdziennikApiMessagesSent(override val data: DataIdziennik,
val sentDate = Date.fromIso(jMessage.get("dataWyslania").asString)
val message = Message(
profileId,
messageId,
subject,
body,
TYPE_SENT,
-1,
-1
profileId = profileId,
id = messageId,
type = TYPE_SENT,
subject = subject,
body = body,
senderId = null
)
for (recipientEl in jMessage.getAsJsonArray("odbiorcy")) {
@ -76,7 +75,7 @@ class IdziennikApiMessagesSent(override val data: DataIdziennik,
data.messageRecipientIgnoreList.add(messageRecipient)
}
data.messageIgnoreList.add(message)
data.messageList.add(message)
data.metadataList.add(Metadata(profileId, Metadata.TYPE_MESSAGE, message.id, true, true, sentDate))
}

View File

@ -10,8 +10,8 @@ import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_GET_MESSAGE
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.MessageGetEvent
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.data.db.full.MessageRecipientFull
@ -50,7 +50,11 @@ class IdziennikWebGetMessage(override val data: DataIdziennik,
message.recipients?.clear()
when (message.type) {
TYPE_RECEIVED -> {
val recipientObject = MessageRecipientFull(profileId, -1, message.id)
val recipientObject = MessageRecipientFull(
profileId = profileId,
id = -1,
messageId = message.id
)
val readDateString = it.getString("DataOdczytania")
recipientObject.readDate = if (readDateString.isNullOrBlank()) System.currentTimeMillis()
@ -67,7 +71,11 @@ class IdziennikWebGetMessage(override val data: DataIdziennik,
val recipientName = recipient.getString("NazwaOdbiorcy") ?: return@forEach
val teacher = data.getTeacherByLastFirst(recipientName)
val recipientObject = MessageRecipientFull(profileId, teacher.id, message.id)
val recipientObject = MessageRecipientFull(
profileId = profileId,
id = teacher.id,
messageId = message.id
)
recipientObject.readDate = recipient.getLong("Status") ?: return@forEach
recipientObject.fullName = teacher.fullName
@ -91,9 +99,10 @@ class IdziennikWebGetMessage(override val data: DataIdziennik,
))
}
EventBus.getDefault().postSticky(MessageGetEvent(message))
data.messageList.add(message)
data.messageListReplace = true
EventBus.getDefault().postSticky(MessageGetEvent(message))
onSuccess()
}
}

View File

@ -57,7 +57,7 @@ class IdziennikWebSendMessage(override val data: DataIdziennik,
}
IdziennikApiMessagesSent(data, null) {
val message = data.messageIgnoreList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject }
val message = data.messageList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject }
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id }
val event = MessageSentEvent(data.profileId, message, metadata?.addedDate)

View File

@ -12,7 +12,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_MESS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_MESSAGES_SENT
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusMessages
import pl.szczodrzynski.edziennik.data.db.entity.*
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.fixName
import pl.szczodrzynski.edziennik.singleOrNull
import pl.szczodrzynski.edziennik.utils.Utils
@ -78,7 +78,7 @@ class LibrusMessagesGetList(override val data: DataLibrus,
val senderId = when (type) {
TYPE_RECEIVED -> recipientId
else -> -1
else -> null
}
val receiverId = when (type) {
@ -92,13 +92,12 @@ class LibrusMessagesGetList(override val data: DataLibrus,
}
val messageObject = Message(
profileId,
id,
subject,
null,
type,
senderId,
-1
profileId = profileId,
id = id,
type = type,
subject = subject,
body = null,
senderId = senderId
)
val messageRecipientObject = MessageRecipient(
@ -111,10 +110,10 @@ class LibrusMessagesGetList(override val data: DataLibrus,
element.select("isAnyFileAttached")?.text()?.let {
if (it == "1")
messageObject.overrideHasAttachments = true
messageObject.hasAttachments = true
}
data.messageIgnoreList.add(messageObject)
data.messageList.add(messageObject)
data.messageRecipientList.add(messageRecipientObject)
data.setSeenMetadataList.add(Metadata(
profileId,

View File

@ -9,8 +9,8 @@ import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusMessages
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
@ -102,11 +102,10 @@ class LibrusMessagesGetMessage(override val data: DataLibrus,
}
val messageRecipientObject = MessageRecipientFull(
profileId,
-1,
-1,
readDate,
messageObject.id
profileId = profileId,
id = -1,
messageId = messageObject.id,
readDate = readDate
)
messageRecipientObject.fullName = profile.accountName ?: profile.studentNameLong ?: ""
@ -132,11 +131,10 @@ class LibrusMessagesGetMessage(override val data: DataLibrus,
}
val messageRecipientObject = MessageRecipientFull(
profileId,
receiverId,
-1,
readDate,
messageObject.id
profileId = profileId,
id = receiverId,
messageId = messageObject.id,
readDate = readDate
)
messageRecipientObject.fullName = "$receiverFirstName $receiverLastName"
@ -159,7 +157,9 @@ class LibrusMessagesGetMessage(override val data: DataLibrus,
messageObject.recipients = messageRecipientList
data.messageRecipientList.addAll(messageRecipientList)
data.messageList.add(messageObject)
data.messageListReplace = true
EventBus.getDefault().postSticky(MessageGetEvent(messageObject))
onSuccess()

View File

@ -48,7 +48,7 @@ class LibrusMessagesSendMessage(override val data: DataLibrus,
}
LibrusMessagesGetList(data, type = Message.TYPE_SENT, lastSync = null) {
val message = data.messageIgnoreList.firstOrNull { it.type == Message.TYPE_SENT && it.id == id }
val message = data.messageList.firstOrNull { it.type == Message.TYPE_SENT && it.id == id }
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id }
val event = MessageSentEvent(data.profileId, message, metadata?.addedDate)

View File

@ -11,7 +11,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidzienn
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.data.db.full.MessageRecipientFull
@ -61,19 +61,17 @@ class MobidziennikWebGetMessage(override val data: DataMobidziennik,
}
val recipient = MessageRecipientFull(
profileId,
-1,
-1,
readDate,
message.id
profileId = profileId,
id = -1,
messageId = message.id,
readDate = readDate
)
recipient.fullName = profile?.accountName ?: profile?.studentNameLong ?: ""
messageRecipientList.add(recipient)
} else {
message.senderId = -1
message.senderReplyId = -1
message.senderId = null
content.select("table.spis tr:has(td)")?.forEach { recipientEl ->
val senderEl = recipientEl.select("td:eq(0)").first()
@ -100,11 +98,10 @@ class MobidziennikWebGetMessage(override val data: DataMobidziennik,
}
val recipient = MessageRecipientFull(
profileId,
receiverId,
-1,
readDate,
message.id
profileId = profileId,
id = receiverId,
messageId = message.id,
readDate = readDate
)
recipient.fullName = teacher?.fullName ?: "?"
@ -149,7 +146,9 @@ class MobidziennikWebGetMessage(override val data: DataMobidziennik,
message.recipients = messageRecipientList
data.messageRecipientList.addAll(messageRecipientList)
data.messageList.add(message)
data.messageListReplace = true
EventBus.getDefault().postSticky(MessageGetEvent(message))
onSuccess()

View File

@ -10,8 +10,8 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidzienn
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.fixName
@ -54,12 +54,12 @@ class MobidziennikWebMessagesAll(override val data: DataMobidziennik,
type = TYPE_SENT
val senderEl = item.select("td:eq(3) div").first()
var senderId: Long = -1
var senderId: Long? = null
if (type == TYPE_RECEIVED) {
// search sender teacher
val senderName = senderEl.text().fixName()
senderId = data.teacherList.singleOrNull { it.fullNameLastFirst == senderName }?.id ?: -1
senderId = data.teacherList.singleOrNull { it.fullNameLastFirst == senderName }?.id
data.messageRecipientList.add(MessageRecipient(profileId, -1, id))
} else {
// TYPE_SENT, so multiple recipients possible
@ -72,16 +72,15 @@ class MobidziennikWebMessagesAll(override val data: DataMobidziennik,
}
val message = Message(
profileId,
id,
subject,
null,
type,
senderId,
-1
profileId = profileId,
id = id,
type = type,
subject = subject,
body = null,
senderId = senderId
)
data.messageIgnoreList.add(message)
data.messageList.add(message)
data.metadataList.add(Metadata(profileId, Metadata.TYPE_MESSAGE, message.id, true, true, addedDate))
}

View File

@ -52,25 +52,24 @@ class MobidziennikWebMessagesInbox(override val data: DataMobidziennik,
val senderEl = item.select("td:eq(2)").first()
val senderName = senderEl.ownText().fixName()
val senderId = data.teacherList.singleOrNull { it.fullNameLastFirst == senderName }?.id ?: -1
val senderId = data.teacherList.singleOrNull { it.fullNameLastFirst == senderName }?.id
data.messageRecipientIgnoreList.add(MessageRecipient(profileId, -1, id))
val isRead = item.select("td:eq(3) span").first().hasClass("wiadomosc_przeczytana")
val message = Message(
profileId,
id,
subject,
null,
Message.TYPE_RECEIVED,
senderId,
-1
profileId = profileId,
id = id,
type = Message.TYPE_RECEIVED,
subject = subject,
body = null,
senderId = senderId
)
if (hasAttachments)
message.setHasAttachments()
message.hasAttachments = true
data.messageIgnoreList.add(message)
data.messageList.add(message)
data.setSeenMetadataList.add(
Metadata(
profileId,

View File

@ -73,19 +73,18 @@ class MobidziennikWebMessagesSent(override val data: DataMobidziennik,
val addedDate = Date.fromIsoHm(addedDateEl.text())
val message = Message(
profileId,
id,
subject,
null,
Message.TYPE_SENT,
-1,
-1
profileId = profileId,
id = id,
type = Message.TYPE_SENT,
subject = subject,
body = null,
senderId = null
)
if (hasAttachments)
message.setHasAttachments()
message.hasAttachments = true
data.messageIgnoreList.add(message)
data.messageList.add(message)
data.setSeenMetadataList.add(
Metadata(
profileId,

View File

@ -43,7 +43,7 @@ class MobidziennikWebSendMessage(override val data: DataMobidziennik,
// TODO create MobidziennikWebMessagesSent and replace this
MobidziennikWebMessagesAll(data, null) {
val message = data.messageIgnoreList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject }
val message = data.messageList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject }
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id }
val event = MessageSentEvent(data.profileId, message, metadata?.addedDate)

View File

@ -9,7 +9,7 @@ import pl.szczodrzynski.edziennik.data.api.VULCAN_API_ENDPOINT_MESSAGES_CHANGE_S
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.full.MessageFull

View File

@ -10,7 +10,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_API_MESSAGES_INBOX
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
import pl.szczodrzynski.edziennik.data.db.entity.*
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.models.Date
import kotlin.text.replace
@ -44,8 +44,7 @@ class VulcanApiMessagesInbox(override val data: DataVulcan,
val body = message.getString("Tresc") ?: ""
val senderLoginId = message.getString("NadawcaId") ?: return@forEach
val senderId = data.teacherList
.singleOrNull { it.loginId == senderLoginId }?.id ?: {
val senderId = data.teacherList.singleOrNull { it.loginId == senderLoginId }?.id ?: {
val senderName = message.getString("Nadawca") ?: ""
@ -60,7 +59,7 @@ class VulcanApiMessagesInbox(override val data: DataVulcan,
data.teacherList.put(teacherObject.id, teacherObject)
teacherObject.id
}
}.invoke() ?: -1
}.invoke()
val sentDate = message.getLong("DataWyslaniaUnixEpoch")?.let { it * 1000 }
?: -1
@ -68,13 +67,12 @@ class VulcanApiMessagesInbox(override val data: DataVulcan,
?: -1
val messageObject = Message(
profileId,
id,
subject,
body.replace("\n", "<br>"),
TYPE_RECEIVED,
senderId,
-1
profileId = profileId,
id = id,
type = TYPE_RECEIVED,
subject = subject,
body = body.replace("\n", "<br>"),
senderId = senderId
)
val messageRecipientObject = MessageRecipient(
@ -85,7 +83,7 @@ class VulcanApiMessagesInbox(override val data: DataVulcan,
id
)
data.messageIgnoreList.add(messageObject)
data.messageList.add(messageObject)
data.messageRecipientList.add(messageRecipientObject)
data.setSeenMetadataList.add(Metadata(
profileId,

View File

@ -11,7 +11,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_API_MESSAGES_SENT
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
@ -92,16 +92,15 @@ class VulcanApiMessagesSent(override val data: DataVulcan,
}
val messageObject = Message(
profileId,
id,
subject,
body.replace("\n", "<br>"),
TYPE_SENT,
-1,
-1
profileId = profileId,
id = id,
type = TYPE_SENT,
subject = subject,
body = body.replace("\n", "<br>"),
senderId = null
)
data.messageIgnoreList.add(messageObject)
data.messageList.add(messageObject)
data.setSeenMetadataList.add(Metadata(
profileId,
Metadata.TYPE_MESSAGE,

View File

@ -52,7 +52,7 @@ class VulcanApiSendMessage(override val data: DataVulcan,
}
VulcanApiMessagesSent(data, null) {
val message = data.messageIgnoreList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject }
val message = data.messageList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject }
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == messageId }
val event = MessageSentEvent(data.profileId, message, metadata?.addedDate)

View File

@ -87,6 +87,7 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
var teacherOnConflictStrategy = OnConflictStrategy.IGNORE
var eventListReplace = false
var messageListReplace = false
val classrooms = LongSparseArray<Classroom>()
val attendanceTypes = LongSparseArray<AttendanceType>()
@ -126,7 +127,6 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
val teacherAbsenceList = mutableListOf<TeacherAbsence>()
val messageList = mutableListOf<Message>()
val messageIgnoreList = mutableListOf<Message>()
val messageRecipientList = mutableListOf<MessageRecipient>()
val messageRecipientIgnoreList = mutableListOf<MessageRecipient>()
@ -182,7 +182,6 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
luckyNumberList.clear()
teacherAbsenceList.clear()
messageList.clear()
messageIgnoreList.clear()
messageRecipientList.clear()
messageRecipientIgnoreList.clear()
metadataList.clear()
@ -305,10 +304,12 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
if (teacherAbsenceList.isNotEmpty())
db.teacherAbsenceDao().addAll(teacherAbsenceList)
if (messageList.isNotEmpty())
db.messageDao().addAll(messageList)
if (messageIgnoreList.isNotEmpty())
db.messageDao().addAllIgnore(messageIgnoreList)
if (messageList.isNotEmpty()) {
if (messageListReplace)
db.messageDao().replaceAll(messageList)
else
db.messageDao().upsertAll(messageList, removeNotKept = false) // TODO dataRemoveModel for messages
}
if (messageRecipientList.isNotEmpty())
db.messageRecipientDao().addAll(messageRecipientList)
if (messageRecipientIgnoreList.isNotEmpty())

View File

@ -227,10 +227,10 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
}
private fun messageNotifications() {
for (message in app.db.messageDao().receivedNotNotifiedNow) {
for (message in app.db.messageDao().getNotNotifiedNow()) {
val text = app.getString(
R.string.notification_message_format,
message.senderFullName,
message.senderName,
message.subject
)
notifications += Notification(

View File

@ -43,7 +43,7 @@ import pl.szczodrzynski.edziennik.data.db.migration.*
LibrusLesson::class,
TimetableManual::class,
Metadata::class
], version = 83)
], version = 84)
@TypeConverters(
ConverterTime::class,
ConverterDate::class,
@ -168,7 +168,8 @@ abstract class AppDb : RoomDatabase() {
Migration80(),
Migration81(),
Migration82(),
Migration83()
Migration83(),
Migration84()
).allowMainThreadQueries().build()
}
}

View File

@ -1,114 +0,0 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
*/
package pl.szczodrzynski.edziennik.data.db.dao;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import androidx.room.RawQuery;
import androidx.sqlite.db.SimpleSQLiteQuery;
import androidx.sqlite.db.SupportSQLiteQuery;
import java.util.List;
import pl.szczodrzynski.edziennik.data.db.entity.Message;
import pl.szczodrzynski.edziennik.data.db.entity.Metadata;
import pl.szczodrzynski.edziennik.data.db.full.MessageFull;
import static pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_DELETED;
import static pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED;
import static pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT;
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_MESSAGE;
@Dao
public abstract class MessageDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
public abstract long add(Message message);
@Insert(onConflict = OnConflictStrategy.REPLACE)
public abstract void addAll(List<Message> messageList);
@Insert(onConflict = OnConflictStrategy.IGNORE)
public abstract void addAllIgnore(List<Message> messageList);
@Query("DELETE FROM messages WHERE profileId = :profileId")
public abstract void clear(int profileId);
@RawQuery(observedEntities = {Message.class})
abstract LiveData<List<MessageFull>> getAll(SupportSQLiteQuery query);
@RawQuery(observedEntities = {Message.class, Metadata.class})
abstract List<MessageFull> getNow(SupportSQLiteQuery query);
@RawQuery(observedEntities = {Message.class, Metadata.class})
abstract MessageFull getOneNow(SupportSQLiteQuery query);
public LiveData<List<MessageFull>> getWithMetadataAndSenderName(int profileId, int messageType, String filter) {
return getAll(new SimpleSQLiteQuery("SELECT \n" +
"*, \n" +
"teachers.teacherName || ' ' || teachers.teacherSurname AS senderFullName\n" +
"FROM messages \n" +
"LEFT JOIN teachers ON teachers.profileId = "+profileId+" AND teacherId = senderId\n" +
"LEFT JOIN metadata ON messageId = thingId AND thingType = "+TYPE_MESSAGE+" AND metadata.profileId = "+profileId+"\n" +
"WHERE messages.profileId = "+profileId+" AND messageType = "+messageType+" AND "+filter+"\n" +
"ORDER BY addedDate DESC"));
}
public LiveData<List<MessageFull>> getWithMetadata(int profileId, int messageType, String filter) {
return getAll(new SimpleSQLiteQuery("SELECT \n" +
"* \n" +
"FROM messages \n" +
"LEFT JOIN metadata ON messageId = thingId AND thingType = "+TYPE_MESSAGE+" AND metadata.profileId = "+profileId+"\n" +
"WHERE messages.profileId = "+profileId+" AND messageType = "+messageType+" AND "+filter+"\n" +
"ORDER BY addedDate DESC"));
}
@Nullable
public MessageFull getById(int profileId, long messageId) {
return getOneNow(new SimpleSQLiteQuery("SELECT \n" +
"*, \n" +
"teachers.teacherName || ' ' || teachers.teacherSurname AS senderFullName\n" +
"FROM messages \n" +
"LEFT JOIN teachers ON teachers.profileId = "+profileId+" AND teacherId = senderId\n" +
"LEFT JOIN metadata ON messageId = thingId AND thingType = "+TYPE_MESSAGE+" AND metadata.profileId = "+profileId+"\n" +
"WHERE messages.profileId = "+profileId+" AND messageId = "+messageId+"\n" +
"ORDER BY addedDate DESC"));
}
public LiveData<List<MessageFull>> getReceived(int profileId) {
return getWithMetadataAndSenderName(profileId, TYPE_RECEIVED, "1");
}
public LiveData<List<MessageFull>> getDeleted(int profileId) {
return getWithMetadataAndSenderName(profileId, TYPE_DELETED, "1");
}
public LiveData<List<MessageFull>> getSent(int profileId) {
return getWithMetadata(profileId, TYPE_SENT, "1");
}
public List<MessageFull> getReceivedNow(int profileId, String filter) {
return getNow(new SimpleSQLiteQuery("SELECT \n" +
"*, \n" +
"teachers.teacherName || ' ' || teachers.teacherSurname AS senderFullName\n" +
"FROM messages \n" +
"LEFT JOIN teachers ON teachers.profileId = "+profileId+" AND teacherId = senderId\n" +
"LEFT JOIN metadata ON messageId = thingId AND thingType = "+TYPE_MESSAGE+" AND metadata.profileId = "+profileId+"\n" +
"WHERE messages.profileId = "+profileId+" AND messageType = 0 AND "+filter+"\n" +
"ORDER BY addedDate DESC"));
}
public List<MessageFull> getReceivedNotNotifiedNow(int profileId) {
return getReceivedNow(profileId, "notified = 0");
}
@Query("SELECT " +
"*, " +
"teachers.teacherName || ' ' || teachers.teacherSurname AS senderFullName " +
"FROM messages " +
"LEFT JOIN teachers ON teachers.profileId = messages.profileId AND teacherId = senderId " +
"LEFT JOIN metadata ON messageId = thingId AND thingType = "+TYPE_MESSAGE+" AND metadata.profileId = messages.profileId " +
"WHERE messageType = 0 AND notified = 0 " +
"ORDER BY addedDate DESC")
public abstract List<MessageFull> getReceivedNotNotifiedNow();
}

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
*/
package pl.szczodrzynski.edziennik.data.db.dao
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Query
import androidx.room.RawQuery
import androidx.sqlite.db.SupportSQLiteQuery
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.annotation.SelectiveDao
import pl.szczodrzynski.edziennik.annotation.UpdateSelective
import pl.szczodrzynski.edziennik.data.db.AppDb
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
@Dao
@SelectiveDao(db = AppDb::class)
abstract class MessageDao : BaseDao<Message, MessageFull> {
companion object {
private const val QUERY = """
SELECT
*,
teachers.teacherName ||" "|| teachers.teacherSurname AS senderName
FROM messages
LEFT JOIN teachers ON teachers.profileId = messages.profileId AND teacherId = senderId
LEFT JOIN metadata ON messageId = thingId AND thingType = ${Metadata.TYPE_MESSAGE} AND metadata.profileId = messages.profileId
"""
private const val ORDER_BY = """ORDER BY messageIsPinned, addedDate DESC"""
}
private val selective by lazy { MessageDaoSelective(App.db) }
@RawQuery(observedEntities = [Message::class])
abstract override fun getRaw(query: SupportSQLiteQuery): LiveData<List<MessageFull>>
@UpdateSelective(primaryKeys = ["profileId", "messageId"], skippedColumns = ["messageType", "messageBody", "messageIsPinned", "attachmentIds", "attachmentNames", "attachmentSizes"])
override fun update(item: Message) = selective.update(item)
override fun updateAll(items: List<Message>) = selective.updateAll(items)
// CLEAR
@Query("DELETE FROM messages WHERE profileId = :profileId")
abstract override fun clear(profileId: Int)
// GET ALL - LIVE DATA
fun getAll(profileId: Int) =
getRaw("$QUERY WHERE messages.profileId = $profileId $ORDER_BY")
fun getAllByType(profileId: Int, type: Int) =
getRaw("$QUERY WHERE messages.profileId = $profileId AND messageType = $type $ORDER_BY")
fun getReceived(profileId: Int) = getAllByType(profileId, Message.TYPE_RECEIVED)
fun getSent(profileId: Int) = getAllByType(profileId, Message.TYPE_SENT)
fun getDeleted(profileId: Int) = getAllByType(profileId, Message.TYPE_DELETED)
fun getDraft(profileId: Int) = getAllByType(profileId, Message.TYPE_DRAFT)
// GET ALL - NOW
fun getAllNow(profileId: Int) =
getRawNow("$QUERY WHERE messages.profileId = $profileId $ORDER_BY")
fun getNotNotifiedNow() =
getRawNow("$QUERY WHERE notified = 0 AND messageType = ${Message.TYPE_RECEIVED} $ORDER_BY")
// GET ONE - NOW
fun getByIdNow(profileId: Int, id: Long) =
getOneNow("$QUERY WHERE messages.profileId = $profileId AND messageId = $id")
}

View File

@ -93,8 +93,8 @@ public abstract class MetadataDao {
}
}
if (o instanceof Message) {
if (add(new Metadata(profileId, TYPE_MESSAGE, ((Message) o).id, seen, false, 0)) == -1) {
updateSeen(profileId, TYPE_MESSAGE, ((Message) o).id, seen);
if (add(new Metadata(profileId, TYPE_MESSAGE, ((Message) o).getId(), seen, false, 0)) == -1) {
updateSeen(profileId, TYPE_MESSAGE, ((Message) o).getId(), seen);
}
}
}
@ -132,8 +132,8 @@ public abstract class MetadataDao {
}
}
if (o instanceof Message) {
if (add(new Metadata(profileId, TYPE_MESSAGE, ((Message) o).id, false, notified, 0)) == -1) {
updateNotified(profileId, TYPE_MESSAGE, ((Message) o).id, notified);
if (add(new Metadata(profileId, TYPE_MESSAGE, ((Message) o).getId(), false, notified, 0)) == -1) {
updateNotified(profileId, TYPE_MESSAGE, ((Message) o).getId(), notified);
}
}
}

View File

@ -1,92 +0,0 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
*/
package pl.szczodrzynski.edziennik.data.db.entity;
import androidx.annotation.Nullable;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.Index;
import java.util.ArrayList;
import java.util.List;
@Entity(tableName = "messages",
primaryKeys = {"profileId", "messageId"},
indices = {@Index(value = {"profileId"})})
public class Message {
public int profileId;
@ColumnInfo(name = "messageId")
public long id;
@ColumnInfo(name = "messageSubject")
public String subject;
@Nullable
@ColumnInfo(name = "messageBody")
public String body = null;
public static final int TYPE_RECEIVED = 0;
public static final int TYPE_SENT = 1;
public static final int TYPE_DELETED = 2;
public static final int TYPE_DRAFT = 3;
@ColumnInfo(name = "messageType")
public int type = TYPE_RECEIVED;
public long senderId = -1; // -1 for sent messages
public long senderReplyId = -1;
public boolean overrideHasAttachments = false; // if the attachments are not yet downloaded but we already know there are some
public List<Long> attachmentIds = null;
public List<String> attachmentNames = null;
public List<Long> attachmentSizes = null;
@Ignore
public Message() {}
public Message(int profileId, long id, String subject, @Nullable String body, int type, long senderId, long senderReplyId) {
this.profileId = profileId;
this.id = id;
this.subject = subject;
this.body = body;
this.type = type;
this.senderId = senderId;
this.senderReplyId = senderReplyId;
}
/**
* Add an attachment
* @param id attachment ID
* @param name file name incl. extension
* @param size file size or -1 if unknown
* @return a Message to which the attachment has been added
*/
public Message addAttachment(long id, String name, long size) {
if (attachmentIds == null)
attachmentIds = new ArrayList<>();
if (attachmentNames == null)
attachmentNames = new ArrayList<>();
if (attachmentSizes == null)
attachmentSizes = new ArrayList<>();
attachmentIds.add(id);
attachmentNames.add(name);
attachmentSizes.add(size);
return this;
}
public void clearAttachments() {
attachmentIds = null;
attachmentNames = null;
attachmentSizes = null;
}
public Message setHasAttachments() {
overrideHasAttachments = true;
return this;
}
public boolean hasAttachments() {
return overrideHasAttachments || (attachmentIds != null && attachmentIds.size() > 0);
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
*/
package pl.szczodrzynski.edziennik.data.db.entity
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.Index
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
@Entity(tableName = "messages",
primaryKeys = ["profileId", "messageId"],
indices = [
Index(value = ["profileId", "messageType"])
])
open class Message(
val profileId: Int,
@ColumnInfo(name = "messageId")
val id: Long,
@ColumnInfo(name = "messageType")
var type: Int,
@ColumnInfo(name = "messageSubject")
var subject: String,
@ColumnInfo(name = "messageBody")
var body: String?,
/**
* Keep in mind that this being null does NOT
* necessarily mean the message is sent.
*/
var senderId: Long?
) : Keepable() {
companion object {
const val TYPE_RECEIVED = 0
const val TYPE_SENT = 1
const val TYPE_DELETED = 2
const val TYPE_DRAFT = 3
}
@ColumnInfo(name = "messageIsPinned")
var isPinned: Boolean = false
var hasAttachments = false // if the attachments are not yet downloaded but we already know there are some
get() = field || attachmentIds.isNotNullNorEmpty()
var attachmentIds: MutableList<Long>? = null
var attachmentNames: MutableList<String>? = null
var attachmentSizes: MutableList<Long>? = null
@Ignore
var showAsUnseen: Boolean? = null
/**
* Add an attachment
* @param id attachment ID
* @param name file name incl. extension
* @param size file size or -1 if unknown
* @return a Message to which the attachment has been added
*/
fun addAttachment(id: Long, name: String, size: Long): Message {
if (attachmentIds == null) attachmentIds = mutableListOf()
if (attachmentNames == null) attachmentNames = mutableListOf()
if (attachmentSizes == null) attachmentSizes = mutableListOf()
attachmentIds?.add(id)
attachmentNames?.add(name)
attachmentSizes?.add(size)
return this
}
fun clearAttachments() {
attachmentIds = null
attachmentNames = null
attachmentSizes = null
}
}

View File

@ -1,32 +0,0 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
*/
package pl.szczodrzynski.edziennik.data.db.full;
import androidx.annotation.Nullable;
import androidx.room.Ignore;
import java.util.ArrayList;
import java.util.List;
import pl.szczodrzynski.edziennik.data.db.entity.Message;
public class MessageFull extends Message {
public String senderFullName = null;
@Ignore
@Nullable
public List<MessageRecipientFull> recipients = null;
public MessageFull addRecipient(MessageRecipientFull recipient) {
if (recipients == null)
recipients = new ArrayList<>();
recipients.add(recipient);
return this;
}
// metadata
public boolean seen;
public boolean notified;
public long addedDate;
}

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
*/
package pl.szczodrzynski.edziennik.data.db.full
import androidx.room.Relation
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
class MessageFull(
profileId: Int, id: Long, type: Int,
subject: String, body: String?, senderId: Long?
) : Message(
profileId, id, type,
subject, body, senderId
) {
var senderName: String? = null
@Relation(parentColumn = "messageId", entityColumn = "messageId", entity = MessageRecipient::class)
var recipients: MutableList<MessageRecipientFull>? = null
fun addRecipient(recipient: MessageRecipientFull): MessageFull {
if (recipients == null) recipients = mutableListOf()
recipients?.add(recipient)
return this
}
// metadata
var seen = false
var notified = false
var addedDate: Long = 0
}

View File

@ -2,17 +2,13 @@ package pl.szczodrzynski.edziennik.data.db.full
import androidx.room.Ignore
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
import pl.szczodrzynski.edziennik.fixName
class MessageRecipientFull : MessageRecipient {
var fullName: String? = ""
get() {
return field?.fixName() ?: ""
}
class MessageRecipientFull(
profileId: Int,
id: Long,
messageId: Long,
readDate: Long = -1L
) : MessageRecipient(profileId, id, -1, readDate, messageId) {
@Ignore
constructor(profileId: Int, id: Long, replyId: Long, readDate: Long, messageId: Long) : super(profileId, id, replyId, readDate, messageId) {}
@Ignore
constructor(profileId: Int, id: Long, messageId: Long) : super(profileId, id, messageId) {}
constructor() : super() {}
var fullName: String? = null
}

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-4-4.
*/
package pl.szczodrzynski.edziennik.data.db.migration
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration84 : Migration(83, 84) {
override fun migrate(database: SupportSQLiteDatabase) {
// The Message Update
database.execSQL("ALTER TABLE messages RENAME TO _messages;")
database.execSQL("""CREATE TABLE messages (
profileId INTEGER NOT NULL,
messageId INTEGER NOT NULL,
messageType INTEGER NOT NULL,
messageSubject TEXT NOT NULL,
messageBody TEXT,
senderId INTEGER,
messageIsPinned INTEGER NOT NULL DEFAULT 0,
hasAttachments INTEGER NOT NULL DEFAULT 0,
attachmentIds TEXT DEFAULT NULL,
attachmentNames TEXT DEFAULT NULL,
attachmentSizes TEXT DEFAULT NULL,
keep INTEGER NOT NULL DEFAULT 1,
PRIMARY KEY(profileId, messageId)
)""")
database.execSQL("DROP INDEX IF EXISTS index_messages_profileId")
database.execSQL("CREATE INDEX index_messages_profileId_messageType ON messages (profileId, messageType)")
database.execSQL("""
INSERT INTO messages (profileId, messageId, messageType, messageSubject, messageBody, senderId, hasAttachments, attachmentIds, attachmentNames, attachmentSizes)
SELECT profileId, messageId, messageType, messageSubject, messageBody,
CASE senderId WHEN -1 THEN NULL ELSE senderId END,
overrideHasAttachments, attachmentIds, attachmentNames, attachmentSizes
FROM _messages
""")
database.execSQL("DROP TABLE _messages")
}
}

View File

@ -12,7 +12,7 @@ import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_MOBIDZIENNIK
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.task.IApiTask
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.getLong
import pl.szczodrzynski.edziennik.getString

View File

@ -71,7 +71,7 @@ class GradesListFragment : Fragment(), CoroutineScope {
val adapter = GradesAdapter(activity)
var firstRun = true
app.db.gradeDao().getAllOrderBy(App.profileId, app.gradesManager.getOrderByString()).observe(this, Observer { items -> launch {
app.db.gradeDao().getAllOrderBy(App.profileId, app.gradesManager.getOrderByString()).observe(this@GradesListFragment, Observer { items -> launch {
if (!isAdded) return@launch
// load & configure the adapter

View File

@ -94,7 +94,7 @@ class HomeworkListFragment : LazyFragment(), CoroutineScope {
}
}
adapter.notifyDataSetChanged()
setSwipeToRefresh(false) // TODO
setSwipeToRefresh(items.isNullOrEmpty())
// show/hide relevant views
b.progressBar.isVisible = false

View File

@ -37,8 +37,8 @@ import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent.Companion.T
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore.Companion.LOGIN_TYPE_IDZIENNIK
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.databinding.MessageFragmentBinding
import pl.szczodrzynski.edziennik.utils.Anim
@ -127,7 +127,7 @@ class MessageFragment : Fragment(), CoroutineScope {
it.addedDate = arguments?.getLong("sentDate") ?: System.currentTimeMillis()
}
else
app.db.messageDao().getById(App.profileId, messageId)
app.db.messageDao().getByIdNow(App.profileId, messageId)
msg?.also {
it.recipients = app.db.messageRecipientDao().getAllByMessageId(it.profileId, it.id)
@ -290,10 +290,10 @@ class MessageFragment : Fragment(), CoroutineScope {
// CREATE VIEWS AND AN OBJECT FOR EVERY ATTACHMENT
message.attachmentNames.forEachIndexed { index, name ->
message.attachmentNames?.forEachIndexed { index, name ->
val messageId = message.id
val id = message.attachmentIds[index]
val size = message.attachmentSizes[index]
val id = message.attachmentIds?.getOrNull(index) ?: return@forEachIndexed
val size = message.attachmentSizes?.getOrNull(index) ?: return@forEachIndexed
// create the parent
val attachmentLayout = FrameLayout(b.root.context)
attachmentLayout.setPadding(16.dp, 0, 16.dp, 0)

View File

@ -2,73 +2,88 @@ package pl.szczodrzynski.edziennik.ui.modules.messages
import android.graphics.Typeface
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView.OnItemClickListener
import androidx.core.view.ViewCompat
import androidx.databinding.DataBindingUtil
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.Adapter
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.Message
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.databinding.MessagesItemBinding
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesAdapter.ViewHolder
import pl.szczodrzynski.edziennik.databinding.MessagesListItemBinding
import pl.szczodrzynski.edziennik.onClick
import pl.szczodrzynski.edziennik.utils.models.Date
import kotlin.coroutines.CoroutineContext
class MessagesAdapter(private val app: App, private val onItemClickListener: OnItemClickListener) : Adapter<ViewHolder>() {
var messageList: List<MessageFull> = ArrayList()
fun setData(messageList: List<MessageFull>) {
this.messageList = messageList
notifyDataSetChanged()
class MessagesAdapter(
val activity: AppCompatActivity,
val teachers: List<Teacher>,
val onItemClick: ((item: MessageFull) -> Unit)? = null
) : RecyclerView.Adapter<MessagesAdapter.ViewHolder>(), CoroutineScope {
companion object {
private const val TAG = "TemplateAdapter"
}
private val app = activity.applicationContext as App
// optional: place the manager here
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
var items = listOf<MessageFull>()
private val typefaceNormal by lazy { Typeface.create(Typeface.DEFAULT, Typeface.NORMAL) }
private val typefaceBold by lazy { Typeface.create(Typeface.DEFAULT, Typeface.BOLD) }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return ViewHolder(DataBindingUtil.inflate(inflater, R.layout.messages_item, parent, false))
val view = MessagesListItemBinding.inflate(inflater, parent, false)
return ViewHolder(view)
}
@Suppress("DEPRECATION")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = items[position]
val b = holder.b
val message = messageList[position]
b.root.setOnClickListener { v: View? -> onItemClickListener.onItemClick(null, v, position, position.toLong()) }
ViewCompat.setTransitionName(b.root, message.id.toString())
b.messageSubject.text = message.subject
b.messageDate.text = Date.fromMillis(message.addedDate).formattedStringShort
b.messageAttachmentImage.visibility = if (message.hasAttachments()) View.VISIBLE else View.GONE
val text = message.body?.substring(0, message.body!!.length.coerceAtMost(200)) ?: ""
b.messageBody.text = MessagesUtils.htmlToSpannable(b.root.context, text)
if (message.type == Message.TYPE_SENT || message.type == Message.TYPE_DRAFT || message.seen) {
b.messageSender.setTextAppearance(b.messageSender.context, R.style.NavView_TextView_Small)
b.messageSender.typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
b.messageSender.textSize = 16f
b.messageSubject.setTextAppearance(b.messageSubject.context, R.style.NavView_TextView_Small)
b.messageSubject.typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
b.messageDate.setTextAppearance(b.messageDate.context, R.style.NavView_TextView_Small)
b.messageDate.typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
} else {
b.messageSender.setTextAppearance(b.messageSender.context, R.style.NavView_TextView_Normal)
b.messageSender.typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
b.messageSender.textSize = 16f
b.messageSubject.setTextAppearance(b.messageSubject.context, R.style.NavView_TextView_Normal)
b.messageSubject.typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
b.messageDate.setTextAppearance(b.messageDate.context, R.style.NavView_TextView_Normal)
b.messageDate.typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
item.recipients?.forEach { recipient ->
if (recipient.fullName == null) {
recipient.fullName = teachers.firstOrNull { it.id == recipient.id }?.fullName ?: ""
}
}
val messageInfo = MessagesUtils.getMessageInfo(app, message, 48, 24, 18, 12)
b.messageSubject.text = item.subject
b.messageDate.text = Date.fromMillis(item.addedDate).formattedStringShort
b.messageAttachmentImage.isVisible = item.hasAttachments
val text = item.body?.take(200) ?: ""
b.messageBody.text = MessagesUtils.htmlToSpannable(activity, text)
val isRead = item.type == Message.TYPE_SENT || item.type == Message.TYPE_DRAFT || item.seen
val typeface = if (isRead) typefaceNormal else typefaceBold
val style = if (isRead) R.style.NavView_TextView_Small else R.style.NavView_TextView_Normal
// set text styles
b.messageSender.setTextAppearance(activity, style)
b.messageSender.typeface = typeface
b.messageSubject.setTextAppearance(activity, style)
b.messageSubject.typeface = typeface
b.messageDate.setTextAppearance(activity, style)
b.messageDate.typeface = typeface
val messageInfo = MessagesUtils.getMessageInfo(app, item, 48, 24, 18, 12)
b.messageProfileBackground.setImageBitmap(messageInfo.profileImage)
b.messageSender.text = messageInfo.profileName
onItemClick?.let { listener ->
b.root.onClick { listener(item) }
}
}
override fun getItemCount(): Int {
return messageList.size
}
inner class ViewHolder(var b: MessagesItemBinding) : RecyclerView.ViewHolder(b.root)
override fun getItemCount() = items.size
class ViewHolder(val b: MessagesListItemBinding) : RecyclerView.ViewHolder(b.root)
}

View File

@ -5,82 +5,79 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.viewpager.widget.ViewPager
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.databinding.FragmentMessagesBinding
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyPagerAdapter
import pl.szczodrzynski.edziennik.utils.Themes
import pl.szczodrzynski.edziennik.databinding.MessagesFragmentBinding
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.FragmentLazyPagerAdapter
import kotlin.coroutines.CoroutineContext
class MessagesFragment : Fragment() {
class MessagesFragment : Fragment(), CoroutineScope {
companion object {
private const val TAG = "MessagesFragment"
var pageSelection = 0
}
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: FragmentMessagesBinding
private lateinit var b: MessagesFragmentBinding
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local/private variables go here
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
if (context == null)
return null
context ?: return null
app = activity.application as App
context!!.theme.applyStyle(Themes.appTheme, true)
if (app.profile == null)
return inflater.inflate(R.layout.fragment_loading, container, false)
// activity, context and profile is valid
b = FragmentMessagesBinding.inflate(inflater)
b = MessagesFragmentBinding.inflate(inflater)
b.refreshLayout.setParent(activity.swipeRefreshLayout)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// TODO check if app, activity, b can be null
if (app.profile == null || !isAdded)
return
if (!isAdded) return
val messageId = arguments?.getLong("messageId", -1L) ?: -1L
if (messageId != -1L) {
val args = Bundle()
args.putLong("messageId", messageId)
arguments!!.remove("messageId")
arguments?.remove("messageId")
activity.loadTarget(MainActivity.TARGET_MESSAGES_DETAILS, args)
return
}
b.viewPager.adapter = Adapter(childFragmentManager, b.refreshLayout).also { adapter ->
val pagerAdapter = FragmentLazyPagerAdapter(
fragmentManager ?: return,
b.refreshLayout,
listOf(
MessagesListFragment().apply {
arguments = Bundle("messageType" to Message.TYPE_RECEIVED)
} to getString(R.string.messages_tab_received),
adapter.addFragment(MessagesListFragment().also { fragment ->
fragment.arguments = Bundle().also { args ->
args.putInt("messageType", Message.TYPE_RECEIVED)
}
}, getString(R.string.menu_messages_inbox))
MessagesListFragment().apply {
arguments = Bundle("messageType" to Message.TYPE_SENT)
} to getString(R.string.messages_tab_sent),
adapter.addFragment(MessagesListFragment().also { fragment ->
fragment.arguments = Bundle().also { args ->
args.putInt("messageType", Message.TYPE_SENT)
}
}, getString(R.string.menu_messages_sent))
}
b.viewPager.currentItem = pageSelection
b.viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) {
pageSelection = position
MessagesListFragment().apply {
arguments = Bundle("messageType" to Message.TYPE_DELETED)
} to getString(R.string.messages_tab_deleted)
)
)
b.viewPager.apply {
offscreenPageLimit = 1
adapter = pagerAdapter
currentItem = pageSelection
addOnPageSelectedListener {
pageSelection = it
}
})
b.tabLayout.setupWithViewPager(b.viewPager)
b.tabLayout.setupWithViewPager(this)
}
activity.navView.apply {
bottomBar.apply {
@ -95,52 +92,5 @@ class MessagesFragment : Fragment() {
}
activity.gainAttentionFAB()
/*if (app.profile.loginStoreType == LOGIN_TYPE_LIBRUS && app.profile.getStudentData("accountPassword", null) == null) {
MaterialDialog.Builder(activity)
.title("Wiadomości w systemie Synergia")
.content("Moduł Wiadomości w aplikacji Szkolny.eu jest przeglądarką zasobów szkolnego konta Synergia. Z tego powodu, musisz wpisać swoje hasło do tego konta, aby móc korzystać z tej funkcji.")
.positiveText(R.string.ok)
.onPositive { dialog, which ->
MaterialDialog.Builder(activity)
.title("Zaloguj się")
.content(Html.fromHtml("Podaj hasło do konta Synergia z loginem <b>" + app.profile.getStudentData("accountLogin", "???") + "</b>"))
.inputType(InputType.TYPE_TEXT_VARIATION_PASSWORD)
.input(null, null) { dialog1, input ->
app.profile.putStudentData("accountPassword", input.toString())
app.profileSaveFullAsync(app.profile)
EdziennikTask.syncProfile(App.profileId, listOf(
DRAWER_ITEM_MESSAGES to Message.TYPE_RECEIVED,
DRAWER_ITEM_MESSAGES to Message.TYPE_SENT
)).enqueue(context!!)
}
.positiveText(R.string.ok)
.negativeText(R.string.cancel)
.show()
}
.show()
}*/
}
internal class Adapter(manager: FragmentManager, swipeRefreshLayout: SwipeRefreshLayout) : LazyPagerAdapter(manager, swipeRefreshLayout) {
private val mFragmentList = mutableListOf<LazyFragment>()
private val mFragmentTitleList = mutableListOf<String>()
override fun getPage(position: Int): LazyFragment {
return mFragmentList[position]
}
override fun getCount(): Int {
return mFragmentList.size
}
fun addFragment(fragment: LazyFragment, title: String) {
mFragmentList.add(fragment)
mFragmentTitleList.add(title)
}
override fun getPageTitle(position: Int): CharSequence {
return mFragmentTitleList[position]
}
}
}

View File

@ -0,0 +1,94 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-4-4.
*/
package pl.szczodrzynski.edziennik.ui.modules.messages
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.withContext
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.databinding.MessagesListFragmentBinding
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import kotlin.coroutines.CoroutineContext
class MessagesListFragment : LazyFragment(), CoroutineScope {
companion object {
private const val TAG = "MessagesListFragment"
}
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: MessagesListFragmentBinding
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local/private variables go here
var teachers = listOf<Teacher>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
context ?: return null
app = activity.application as App
b = MessagesListFragmentBinding.inflate(inflater)
return b.root
}
override fun onPageCreated(): Boolean { startCoroutineTimer(100L) {
val messageType = arguments.getInt("messageType", Message.TYPE_RECEIVED)
teachers = withContext(Dispatchers.Default) {
app.db.teacherDao().getAllNow(App.profileId)
}
val adapter = MessagesAdapter(activity, teachers) {
}
app.db.messageDao().getAllByType(App.profileId, messageType).observe(this@MessagesListFragment, Observer { items ->
if (!isAdded) return@Observer
items.forEach { message ->
message.recipients?.removeAll { it.profileId != message.profileId }
}
// load & configure the adapter
adapter.items = items
if (items.isNotNullNorEmpty() && b.list.adapter == null) {
b.list.adapter = adapter
b.list.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(context)
addItemDecoration(SimpleDividerItemDecoration(context))
if (messageType in Message.TYPE_RECEIVED..Message.TYPE_SENT)
addOnScrollListener(onScrollListener)
}
}
adapter.notifyDataSetChanged()
setSwipeToRefresh(messageType in Message.TYPE_RECEIVED..Message.TYPE_SENT && items.isNullOrEmpty())
// show/hide relevant views
b.progressBar.isVisible = false
if (items.isNullOrEmpty()) {
b.list.isVisible = false
b.noData.isVisible = true
} else {
b.list.isVisible = true
b.noData.isVisible = false
}
})
}; return true }
}

View File

@ -33,7 +33,7 @@ import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
import static pl.szczodrzynski.edziennik.utils.Utils.d;
public class MessagesListFragment extends LazyFragment {
public class MessagesListFragmentOld extends LazyFragment {
private App app = null;
private MainActivity activity = null;
@ -90,19 +90,19 @@ public class MessagesListFragment extends LazyFragment {
activity.syncCurrentFeature(messageType, b.refreshLayout);
});*/
messagesAdapter = new MessagesAdapter(app, ((parent, view1, position, id) -> {
/*messagesAdapter = new MessagesAdapter(app, ((parent, view1, position, id) -> {
// TODO ANIMATION
tapPositions[messageType] = position;
topPositions[messageType] = ((LinearLayoutManager) b.emailList.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
bottomPositions[messageType] = ((LinearLayoutManager) b.emailList.getLayoutManager()).findLastCompletelyVisibleItemPosition();
/*view1.getGlobalVisibleRect(viewRect);
*//*view1.getGlobalVisibleRect(viewRect);
((Transition) MessagesListFragment.this.getExitTransition()).setEpicenterCallback(new Transition.EpicenterCallback() {
@Override
public Rect onGetEpicenter(@NonNull Transition transition) {
return viewRect;
}
});*/
});*//*
Bundle args = new Bundle();
args.putLong("messageId", messagesAdapter.getMessageList().get(position).id);
@ -110,7 +110,7 @@ public class MessagesListFragment extends LazyFragment {
// KOD W WERSJI 2.7
// TODO ANIMATION
/*TransitionSet sharedElementTransition = new TransitionSet()
*//*TransitionSet sharedElementTransition = new TransitionSet()
.addTransition(new Fade())
.addTransition(new ChangeBounds())
.addTransition(new ChangeTransform())
@ -123,10 +123,10 @@ public class MessagesListFragment extends LazyFragment {
args.putLong("messageId", messagesAdapter.messageList.get(position).id);
fragment.setArguments(args);
fragment.setSharedElementEnterTransition(sharedElementTransition);
fragment.setSharedElementReturnTransition(sharedElementTransition);*/
fragment.setSharedElementReturnTransition(sharedElementTransition);*//*
// JAKIS STARSZY KOD
/*Intent intent = new Intent(activity, MessagesDetailsActivity.class);
*//*Intent intent = new Intent(activity, MessagesDetailsActivity.class);
intent.putExtra("item_id", 1);
intent.putExtra("transition_name", ViewCompat.getTransitionName(view1));
@ -143,17 +143,17 @@ public class MessagesListFragment extends LazyFragment {
setExitTransition(sharedElementTransition);
setSharedElementEnterTransition(sharedElementTransition);
setSharedElementReturnTransition(sharedElementTransition);
startActivity(intent, options.toBundle());*/
startActivity(intent, options.toBundle());*//*
/*activity.getSupportFragmentManager()
*//*activity.getSupportFragmentManager()
.beginTransaction()
.setReorderingAllowed(true)
.replace(R.id.fragment_container, fragment)
.addToBackStack(null)
.addSharedElement(view1, getString(R.string.transition_name))
.commit();*/
.commit();*//*
}));
}));*/
//tapPosition = savedInstanceState != null ? savedInstanceState.getInt(TAP_POSITION, tapPosition) : tapPosition;
@ -195,7 +195,7 @@ public class MessagesListFragment extends LazyFragment {
List<MessageRecipientFull> messageRecipients = App.db.messageRecipientDao().getAll(App.Companion.getProfileId());
List<Long> messageIds = new ArrayList<>();
for (MessageFull messageFull: messageFulls) {
messageIds.add(messageFull.id);
messageIds.add(messageFull.getId());
}
for (MessageRecipientFull messageRecipientFull: messageRecipients) {
if (messageRecipientFull.id == -1)
@ -233,7 +233,7 @@ public class MessagesListFragment extends LazyFragment {
private void createMessageList(List<MessageFull> messageFulls) {
b.progressBar.setVisibility(View.GONE);
b.emailList.setVisibility(View.VISIBLE);
messagesAdapter.setData(messageFulls);
//messagesAdapter.setData(messageFulls);
LinearLayoutManager layoutManager = (LinearLayoutManager) b.emailList.getLayoutManager();
if (tapPositions[messageType] != NO_POSITION && layoutManager != null) {

View File

@ -124,7 +124,7 @@ object MessagesUtils {
var profileImage: Bitmap? = null
var profileName: String? = null
if (message.type == Message.TYPE_RECEIVED || message.type == Message.TYPE_DELETED) {
profileName = message.senderFullName?.fixName()
profileName = message.senderName?.fixName()
profileImage = getProfileImage(diameterDp, textSizeBigDp, textSizeMediumDp, textSizeSmallDp, 1, profileName)
} else if (message.type == Message.TYPE_SENT || message.type == Message.TYPE_DRAFT && message.recipients != null) {
when (val count = message.recipients?.size ?: 0) {

View File

@ -1,75 +0,0 @@
package pl.szczodrzynski.edziennik.ui.modules.messages;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.transition.TransitionValues;
import androidx.transition.Visibility;
public class SlideExplode extends Visibility {
private static final String KEY_SCREEN_BOUNDS = "screenBounds";
private int[] mTempLoc = new int[2];
private void captureValues(TransitionValues transitionValues) {
View view = transitionValues.view;
view.getLocationOnScreen(mTempLoc);
int left = mTempLoc[0];
int top = mTempLoc[1];
int right = left + view.getWidth();
int bottom = top + view.getHeight();
transitionValues.values.put(KEY_SCREEN_BOUNDS, new Rect(left, top, right, bottom));
}
@Override
public void captureStartValues(@NonNull TransitionValues transitionValues) {
super.captureStartValues(transitionValues);
captureValues(transitionValues);
}
@Override
public void captureEndValues(@NonNull TransitionValues transitionValues) {
super.captureEndValues(transitionValues);
captureValues(transitionValues);
}
@Override
public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) {
if (endValues == null)
return null;
Rect bounds = (Rect) endValues.values.get(KEY_SCREEN_BOUNDS);
float endY = view.getTranslationY();
float startY = endY + calculateDistance(sceneRoot, bounds);
return ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, startY, endY);
}
@Override
public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) {
if (startValues == null)
return null;
Rect bounds = (Rect) startValues.values.get(KEY_SCREEN_BOUNDS);
float startY = view.getTranslationY();
float endY = startY + calculateDistance(sceneRoot, bounds);
return ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, startY, endY);
}
private int calculateDistance(View sceneRoot, Rect viewBounds) {
sceneRoot.getLocationOnScreen(mTempLoc);
int sceneRootY = mTempLoc[1];
if (getEpicenter() == null) {
return -sceneRoot.getHeight();
}
else if (viewBounds.top <= getEpicenter().top) {
return sceneRootY - getEpicenter().top;
}
else {
return sceneRootY + sceneRoot.getHeight() - getEpicenter().bottom;
}
}
}

View File

@ -1,18 +0,0 @@
package pl.szczodrzynski.edziennik.ui.modules.messages;
import android.animation.TimeInterpolator;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.transition.TransitionSet;
public class Transitions extends TransitionSet {
@NonNull
@Override
public TransitionSet setInterpolator(@Nullable TimeInterpolator interpolator) {
for (int i = 0; i < getTransitionCount(); i++) {
getTransitionAt(i).setInterpolator(interpolator);
}
return this;
}
}

View File

@ -1,8 +1,8 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-12-22.
* Copyright (c) Kuba Szczodrzyński 2020-4-4.
*/
package pl.szczodrzynski.edziennik.ui.modules.messages
package pl.szczodrzynski.edziennik.ui.modules.messages.compose
import android.content.Context
import android.graphics.Typeface
@ -43,6 +43,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.databinding.MessagesComposeFragmentBinding
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesUtils
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesUtils.getProfileImage
import pl.szczodrzynski.edziennik.utils.Colors
import pl.szczodrzynski.edziennik.utils.Themes
@ -361,7 +362,7 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
span.appendText("W dniu ")
span.appendSpan(dateString, StyleSpan(Typeface.ITALIC), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
span.appendText(", ")
span.appendSpan(msg.senderFullName.fixName(), StyleSpan(Typeface.ITALIC), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
span.appendSpan(msg.senderName.fixName(), StyleSpan(Typeface.ITALIC), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
span.appendText(" napisał(a):")
span.setSpan(StyleSpan(Typeface.BOLD), 0, span.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
span.appendText("\n\n")
@ -380,7 +381,8 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
span.replace(0, 0, "\n\n")
subject = "Fwd: ${msg.subject}"
}
body = MessagesUtils.htmlToSpannable(activity,msg.body ?: "Nie udało się wczytać oryginalnej wiadomości.")//Html.fromHtml(msg.body?.replace("<br\\s?/?>".toRegex(), "\n") ?: "Nie udało się wczytać oryginalnej wiadomości.")
body = MessagesUtils.htmlToSpannable(activity, msg.body
?: "Nie udało się wczytać oryginalnej wiadomości.")//Html.fromHtml(msg.body?.replace("<br\\s?/?>".toRegex(), "\n") ?: "Nie udało się wczytać oryginalnej wiadomości.")
}
b.recipients.addTextWithChips(chipList)

View File

@ -1,4 +1,8 @@
package pl.szczodrzynski.edziennik.ui.modules.messages
/*
* Copyright (c) Kuba Szczodrzyński 2020-4-4.
*/
package pl.szczodrzynski.edziennik.ui.modules.messages.compose
class MessagesComposeInfo(
/**
@ -18,4 +22,4 @@ class MessagesComposeInfo(
* -1 means unlimited length.
*/
var maxBodyLength: Int
)
)

View File

@ -1,4 +1,4 @@
package pl.szczodrzynski.edziennik.ui.modules.messages
package pl.szczodrzynski.edziennik.ui.modules.messages.compose
import android.content.Context
import android.graphics.Typeface.BOLD

View File

@ -72,7 +72,7 @@ class NotificationsListFragment : Fragment(), CoroutineScope {
app.sendBroadcast(intent)
}
app.db.notificationDao().getAll().observe(this, Observer { items ->
app.db.notificationDao().getAll().observe(this@NotificationsListFragment, Observer { items ->
if (!isAdded) return@Observer
// load & configure the adapter

View File

@ -12,12 +12,11 @@ import androidx.fragment.app.Fragment
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.addOnPageSelectedListener
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.databinding.TemplateFragmentBinding
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.FragmentLazyPagerAdapter
import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment
import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkDate
import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkListFragment
import kotlin.coroutines.CoroutineContext
class TemplateFragment : Fragment(), CoroutineScope {
@ -52,6 +51,14 @@ class TemplateFragment : Fragment(), CoroutineScope {
fragmentManager ?: return,
b.refreshLayout,
listOf(
HomeworkListFragment().apply {
arguments = Bundle("homeworkDate" to HomeworkDate.CURRENT)
} to getString(R.string.homework_tab_current),
HomeworkListFragment().apply {
arguments = Bundle("homeworkDate" to HomeworkDate.PAST)
} to getString(R.string.homework_tab_past),
TemplatePageFragment() to "Pager 0",
TemplatePageFragment() to "Pager 1",
TemplatePageFragment() to "Pager 2",
@ -67,7 +74,7 @@ class TemplateFragment : Fragment(), CoroutineScope {
adapter = pagerAdapter
currentItem = pageSelection
addOnPageSelectedListener {
HomeworkFragment.pageSelection = it
pageSelection = it
}
b.tabLayout.setupWithViewPager(this)
}

View File

@ -49,7 +49,7 @@ class TemplateListFragment : Fragment(), CoroutineScope {
val adapter = TemplateAdapter(activity)
app.db.notificationDao().getAll().observe(this, Observer { items ->
app.db.notificationDao().getAll().observe(this@TemplateListFragment, Observer { items ->
if (!isAdded) return@Observer
// load & configure the adapter

View File

@ -49,7 +49,7 @@ class TemplateListPageFragment : LazyFragment(), CoroutineScope {
override fun onPageCreated(): Boolean { startCoroutineTimer(100L) {
val adapter = TemplateAdapter(activity)
app.db.notificationDao().getAll().observe(this, Observer { items ->
app.db.notificationDao().getAll().observe(this@TemplateListPageFragment, Observer { items ->
if (!isAdded) return@Observer
// load & configure the adapter
@ -64,7 +64,7 @@ class TemplateListPageFragment : LazyFragment(), CoroutineScope {
}
}
adapter.notifyDataSetChanged()
setSwipeToRefresh(false) // TODO
setSwipeToRefresh(items.isNullOrEmpty())
// show/hide relevant views
b.progressBar.isVisible = false

View File

@ -0,0 +1,10 @@
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-4-4.
-->
<vector android:height="128dp" android:viewportHeight="512"
android:viewportWidth="512" android:width="128dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#f4a402" android:pathData="m440,416h-368c-22.1,0 -40,-17.9 -40,-40v-240c0,-22.1 17.9,-40 40,-40h368c22.1,0 40,17.9 40,40v240c0,22.1 -17.9,40 -40,40z"/>
<path android:fillColor="#feb600" android:pathData="m256,200 l-223,183c3.21,18.9 19.5,33.3 39.3,33.3h368c19.8,0 36.1,-14.4 39.3,-33.3l-223,-183"/>
<path android:fillColor="#ffc71c" android:pathData="m32.7,129 l206,169c10.1,8.24 24.6,8.24 34.6,0l206,-169c-3.21,-18.9 -19.5,-33.3 -39.3,-33.3h-368c-19.8,0 -36.1,14.4 -39.3,33.3z"/>
</vector>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-4-4.
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator
android:id="@+id/refreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorSurface_6dp"
app:tabIndicatorColor="?colorPrimary"
app:tabMode="auto"
app:tabSelectedTextColor="?colorPrimary"
app:tabTextColor="?android:textColorPrimary" />
<pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</LinearLayout>
</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>
</layout>

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-4-4.
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/noData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawablePadding="16dp"
android:fontFamily="sans-serif-light"
android:text="@string/messages_no_data"
android:textSize="24sp"
android:visibility="gone"
app:drawableTopCompat="@drawable/ic_no_messages"
tools:visibility="visible" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
tools:listitem="@layout/messages_list_item"
tools:visibility="visible" />
</FrameLayout>
</layout>

View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-4-4.
-->
<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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground">
<ImageView
android:id="@+id/messageProfileBackground"
android:layout_width="72dp"
android:layout_height="72dp"
android:padding="12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/bg_circle" />
<TextView
android:id="@+id/messageProfileName"
android:layout_width="72dp"
android:layout_height="72dp"
android:gravity="center"
android:padding="12dp"
android:textColor="#ffffff"
android:textSize="24sp"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="JP"
tools:visibility="visible" />
<TextView
android:id="@+id/messageSubject"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:singleLine="true"
android:textStyle="normal"
android:textAppearance="@style/NavView.TextView.Helper"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/messageProfileBackground"
app:layout_constraintTop_toBottomOf="@+id/messageSender"
tools:text="Nowe oferty w Twoich obserwowanych wyszukiwaniach" />
<TextView
android:id="@+id/messageSender"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
android:ellipsize="end"
android:maxLines="2"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@+id/messageAttachmentImage"
app:layout_constraintStart_toEndOf="@+id/messageProfileBackground"
app:layout_constraintTop_toTopOf="parent"
tools:text="Allegro" />
<TextView
android:id="@+id/messageBody"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="12dp"
android:singleLine="true"
android:textAppearance="@style/NavView.TextView.Helper"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/messageProfileBackground"
app:layout_constraintTop_toBottomOf="@+id/messageSubject"
tools:text="Znajdź produkty, których szukasz. Witaj Kuba Szczodrzyński (Client" />
<TextView
android:id="@+id/messageDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="09:41" />
<com.mikepenz.iconics.view.IconicsImageView
android:id="@+id/messageAttachmentImage"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
android:adjustViewBounds="true"
android:paddingTop="2dp"
android:paddingBottom="2dp"
android:scaleType="fitCenter"
app:iiv_color="?android:textColorSecondary"
app:iiv_icon="cmd-attachment"
app:layout_constraintBottom_toBottomOf="@+id/messageDate"
app:layout_constraintEnd_toStartOf="@+id/messageDate"
app:layout_constraintTop_toTopOf="@+id/messageDate"
tools:srcCompat="@tools:sample/avatars[4]" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@ -414,8 +414,8 @@
<string name="menu_manage_profiles">Manage profiles</string>
<string name="menu_mark_as_read">Mark as read</string>
<string name="menu_messages">Messages</string>
<string name="menu_messages_inbox">Inbox</string>
<string name="menu_messages_sent">Sent</string>
<string name="messages_tab_received">Inbox</string>
<string name="messages_tab_sent">Sent</string>
<string name="menu_notices">Behaviour</string>
<string name="menu_notifications">Notifications</string>
<string name="menu_settings">Settings</string>

View File

@ -458,8 +458,8 @@
<string name="menu_mark_as_read">Oznacz jako przeczytane</string>
<string name="menu_mark_everything_as_read">Oznacz wszystko jako przeczytane</string>
<string name="menu_messages">Wiadomości</string>
<string name="menu_messages_inbox">Odebrane</string>
<string name="menu_messages_sent">Wysłane</string>
<string name="messages_tab_received">Odebrane</string>
<string name="messages_tab_sent">Wysłane</string>
<string name="menu_notices">Zachowanie</string>
<string name="menu_notifications">Powiadomienia</string>
<string name="menu_set_student_number">Ustaw numer w dzienniku</string>
@ -1287,4 +1287,6 @@
<string name="dialog_event_details_attachments">Załączniki</string>
<string name="hint_download_again">Pobierz ponownie</string>
<string name="menu_lab">Laboratorium</string>
<string name="messages_no_data">Nie masz żadnych wiadomości.</string>
<string name="messages_tab_deleted">Usunięte</string>
</resources>

View File

@ -158,9 +158,12 @@ class FileGenerator : AbstractProcessor() {
allFields.removeAll { skippedColumns.contains(it.name()) }
allFields.removeAll { it.getAnnotation(Ignore::class.java) != null }
allFields.removeAll { field -> field.modifiers.any { it == Modifier.STATIC || it == Modifier.FINAL } }
allFields.removeAll { field -> field.modifiers.any { it == Modifier.STATIC } }
val allFieldsDistinct = allFields.distinct()
// dump fields
//processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, allFieldsDistinct.joinToString())
val fields = allFieldsDistinct.filterNot { primaryKeys.contains(it.name()) }
val primaryFields = allFieldsDistinct.filter { primaryKeys.contains(it.name()) }
val fieldNames = fields.map { it.name() }