mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-01-18 12:56:45 -06:00
[UI] Implement homework searching. (#93)
* [Messages] Create message type checking methods. * [UI] Refactor messages searching to a separate module. * [Refactor] Move dialogs.event to modules package. * [Refactor] Move classes from modules.messages to separate packages. * [Homework] Implement searching homework lists. * [Homework] Fix highlighting search query in addedBy text. * [Homework] Workaround IconicsTextView discarding span data. * [Messages] Make attachments searchable. * [Events] Show icons for events with attachments. * [Homework] Workaround IconicsTextView discarding span data, again. * [Search] Fix serialization crashes with searchable models. * [Messages] Fix searching in HTML body.
This commit is contained in:
parent
50ae767fcd
commit
1a543814f4
@ -52,7 +52,6 @@ import pl.szczodrzynski.edziennik.ui.dialogs.RegisterUnavailableDialog
|
|||||||
import pl.szczodrzynski.edziennik.ui.dialogs.ServerMessageDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.ServerMessageDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.UpdateAvailableDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.UpdateAvailableDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
|
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.profile.ProfileConfigDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.profile.ProfileConfigDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.sync.SyncViewListDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.sync.SyncViewListDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.AgendaFragment
|
import pl.szczodrzynski.edziennik.ui.modules.agenda.AgendaFragment
|
||||||
@ -64,15 +63,16 @@ import pl.szczodrzynski.edziennik.ui.modules.debug.DebugFragment
|
|||||||
import pl.szczodrzynski.edziennik.ui.modules.debug.LabFragment
|
import pl.szczodrzynski.edziennik.ui.modules.debug.LabFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorDetailsDialog
|
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorDetailsDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar
|
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.event.EventManualDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackFragment
|
import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesListFragment
|
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesListFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment
|
import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
|
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment
|
import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity
|
import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessageFragment
|
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
|
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.compose.MessagesComposeFragment
|
import pl.szczodrzynski.edziennik.ui.modules.messages.compose.MessagesComposeFragment
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.messages.list.MessagesFragment
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.messages.single.MessageFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsListFragment
|
import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsListFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment
|
import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsFragment
|
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsFragment
|
||||||
|
@ -48,7 +48,7 @@ class LibrusMessagesSendMessage(override val data: DataLibrus,
|
|||||||
}
|
}
|
||||||
|
|
||||||
LibrusMessagesGetList(data, type = Message.TYPE_SENT, lastSync = null) {
|
LibrusMessagesGetList(data, type = Message.TYPE_SENT, lastSync = null) {
|
||||||
val message = data.messageList.firstOrNull { it.type == Message.TYPE_SENT && it.id == id }
|
val message = data.messageList.firstOrNull { it.isSent && it.id == id }
|
||||||
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id }
|
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id }
|
||||||
val event = MessageSentEvent(data.profileId, message, message?.addedDate)
|
val event = MessageSentEvent(data.profileId, message, message?.addedDate)
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ class MobidziennikWebGetAttachment(override val data: DataMobidziennik,
|
|||||||
val targetFile = File(Utils.getStorageDir(), attachmentName)
|
val targetFile = File(Utils.getStorageDir(), attachmentName)
|
||||||
|
|
||||||
val typeUrl = when (owner) {
|
val typeUrl = when (owner) {
|
||||||
is Message -> if (owner.type == Message.TYPE_SENT)
|
is Message -> if (owner.isSent)
|
||||||
"dziennik/wiadwyslana/?id="
|
"dziennik/wiadwyslana/?id="
|
||||||
else
|
else
|
||||||
"dziennik/wiadodebrana/?id="
|
"dziennik/wiadodebrana/?id="
|
||||||
|
@ -10,8 +10,6 @@ import pl.szczodrzynski.edziennik.data.api.Regexes
|
|||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
|
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
|
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
|
||||||
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
|
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
|
||||||
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.entity.Metadata
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.MessageRecipientFull
|
import pl.szczodrzynski.edziennik.data.db.full.MessageRecipientFull
|
||||||
@ -31,7 +29,7 @@ class MobidziennikWebGetMessage(override val data: DataMobidziennik,
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val typeUrl = if (message.type == Message.TYPE_SENT)
|
val typeUrl = if (message.isSent)
|
||||||
"wiadwyslana"
|
"wiadwyslana"
|
||||||
else
|
else
|
||||||
"wiadodebrana"
|
"wiadodebrana"
|
||||||
@ -46,7 +44,7 @@ class MobidziennikWebGetMessage(override val data: DataMobidziennik,
|
|||||||
|
|
||||||
val body = content.select(".wiadomosc_tresc").first()
|
val body = content.select(".wiadomosc_tresc").first()
|
||||||
|
|
||||||
if (message.type == TYPE_RECEIVED) {
|
if (message.isReceived) {
|
||||||
var readDate = System.currentTimeMillis()
|
var readDate = System.currentTimeMillis()
|
||||||
Regexes.MOBIDZIENNIK_MESSAGE_READ_DATE.find(body.html())?.let {
|
Regexes.MOBIDZIENNIK_MESSAGE_READ_DATE.find(body.html())?.let {
|
||||||
val date = Date(
|
val date = Date(
|
||||||
|
@ -9,7 +9,6 @@ import pl.szczodrzynski.edziennik.data.api.POST
|
|||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
|
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
|
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
|
||||||
import pl.szczodrzynski.edziennik.data.api.events.MessageSentEvent
|
import pl.szczodrzynski.edziennik.data.api.events.MessageSentEvent
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||||
|
|
||||||
@ -43,7 +42,7 @@ class MobidziennikWebSendMessage(override val data: DataMobidziennik,
|
|||||||
|
|
||||||
// TODO create MobidziennikWebMessagesSent and replace this
|
// TODO create MobidziennikWebMessagesSent and replace this
|
||||||
MobidziennikWebMessagesAll(data, null) {
|
MobidziennikWebMessagesAll(data, null) {
|
||||||
val message = data.messageList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject }
|
val message = data.messageList.firstOrNull { it.isSent && it.subject == subject }
|
||||||
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id }
|
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id }
|
||||||
val event = MessageSentEvent(data.profileId, message, message?.addedDate)
|
val event = MessageSentEvent(data.profileId, message, message?.addedDate)
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ import pl.szczodrzynski.edziennik.data.api.VULCAN_HEBE_ENDPOINT_MESSAGES_STATUS
|
|||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
|
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanHebe
|
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanHebe
|
||||||
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
|
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
|
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||||
@ -48,7 +47,7 @@ class VulcanHebeMessagesChangeStatus(
|
|||||||
messageObject.seen = true
|
messageObject.seen = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messageObject.type != Message.TYPE_SENT) {
|
if (!messageObject.isSent) {
|
||||||
val messageRecipientObject = MessageRecipient(
|
val messageRecipientObject = MessageRecipient(
|
||||||
profileId,
|
profileId,
|
||||||
-1,
|
-1,
|
||||||
|
@ -87,7 +87,7 @@ class VulcanHebeSendMessage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
VulcanHebeMessages(data, null) {
|
VulcanHebeMessages(data, null) {
|
||||||
val message = data.messageList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject }
|
val message = data.messageList.firstOrNull { it.isSent && it.subject == subject }
|
||||||
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == messageId }
|
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == messageId }
|
||||||
val event = MessageSentEvent(data.profileId, message, message?.addedDate)
|
val event = MessageSentEvent(data.profileId, message, message?.addedDate)
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import androidx.room.Index
|
|||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import pl.szczodrzynski.edziennik.MINUTE
|
import pl.szczodrzynski.edziennik.MINUTE
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||||
|
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -97,6 +98,8 @@ open class Event(
|
|||||||
* or the topic contains the body already.
|
* or the topic contains the body already.
|
||||||
*/
|
*/
|
||||||
var homeworkBody: String? = null
|
var homeworkBody: String? = null
|
||||||
|
val hasAttachments
|
||||||
|
get() = attachmentIds.isNotNullNorEmpty()
|
||||||
var attachmentIds: MutableList<Long>? = null
|
var attachmentIds: MutableList<Long>? = null
|
||||||
var attachmentNames: MutableList<String>? = null
|
var attachmentNames: MutableList<String>? = null
|
||||||
|
|
||||||
|
@ -43,6 +43,15 @@ open class Message(
|
|||||||
@ColumnInfo(name = "messageIsPinned")
|
@ColumnInfo(name = "messageIsPinned")
|
||||||
var isStarred: Boolean = false
|
var isStarred: Boolean = false
|
||||||
|
|
||||||
|
val isReceived
|
||||||
|
get() = type == TYPE_RECEIVED
|
||||||
|
val isSent
|
||||||
|
get() = type == TYPE_SENT
|
||||||
|
val isDeleted
|
||||||
|
get() = type == TYPE_DELETED
|
||||||
|
val isDraft
|
||||||
|
get() = type == TYPE_DRAFT
|
||||||
|
|
||||||
var hasAttachments = false // if the attachments are not yet downloaded but we already know there are some
|
var hasAttachments = false // if the attachments are not yet downloaded but we already know there are some
|
||||||
get() = field || attachmentIds.isNotNullNorEmpty()
|
get() = field || attachmentIds.isNotNullNorEmpty()
|
||||||
var attachmentIds: MutableList<Long>? = null
|
var attachmentIds: MutableList<Long>? = null
|
||||||
|
@ -3,8 +3,10 @@
|
|||||||
*/
|
*/
|
||||||
package pl.szczodrzynski.edziennik.data.db.full
|
package pl.szczodrzynski.edziennik.data.db.full
|
||||||
|
|
||||||
|
import androidx.room.Ignore
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.search.Searchable
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||||
|
|
||||||
@ -16,7 +18,7 @@ class EventFull(
|
|||||||
profileId, id, date, time,
|
profileId, id, date, time,
|
||||||
topic, color, type,
|
topic, color, type,
|
||||||
teacherId, subjectId, teamId, addedDate
|
teacherId, subjectId, teamId, addedDate
|
||||||
) {
|
), Searchable<EventFull> {
|
||||||
constructor(event: Event, metadata: Metadata? = null) : this(
|
constructor(event: Event, metadata: Metadata? = null) : this(
|
||||||
event.profileId, event.id, event.date, event.time,
|
event.profileId, event.id, event.date, event.time,
|
||||||
event.topic, event.color, event.type,
|
event.topic, event.color, event.type,
|
||||||
@ -46,6 +48,46 @@ class EventFull(
|
|||||||
var teamName: String? = null
|
var teamName: String? = null
|
||||||
var teamCode: String? = null
|
var teamCode: String? = null
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
@Transient
|
||||||
|
override var searchPriority = 0
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
@Transient
|
||||||
|
override var searchHighlightText: String? = null
|
||||||
|
|
||||||
|
@delegate:Ignore
|
||||||
|
@delegate:Transient
|
||||||
|
override val searchKeywords by lazy {
|
||||||
|
listOf(
|
||||||
|
listOf(topic, homeworkBody),
|
||||||
|
attachmentNames,
|
||||||
|
listOf(subjectLongName),
|
||||||
|
listOf(teacherName),
|
||||||
|
listOf(sharedByName),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun compareTo(other: Searchable<*>): Int {
|
||||||
|
if (other !is EventFull)
|
||||||
|
return 0
|
||||||
|
return when {
|
||||||
|
// ascending sorting
|
||||||
|
searchPriority > other.searchPriority -> 1
|
||||||
|
searchPriority < other.searchPriority -> -1
|
||||||
|
// ascending sorting
|
||||||
|
date > other.date -> 1
|
||||||
|
date < other.date -> -1
|
||||||
|
// ascending sorting
|
||||||
|
(time?.value ?: 0) > (other.time?.value ?: 0) -> 1
|
||||||
|
(time?.value ?: 0) < (other.time?.value ?: 0) -> -1
|
||||||
|
// ascending sorting
|
||||||
|
addedDate > other.addedDate -> 1
|
||||||
|
addedDate < other.addedDate -> -1
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// metadata
|
// metadata
|
||||||
var seen = false
|
var seen = false
|
||||||
var notified = false
|
var notified = false
|
||||||
|
@ -3,10 +3,12 @@
|
|||||||
*/
|
*/
|
||||||
package pl.szczodrzynski.edziennik.data.db.full
|
package pl.szczodrzynski.edziennik.data.db.full
|
||||||
|
|
||||||
|
import androidx.core.text.HtmlCompat
|
||||||
import androidx.room.Ignore
|
import androidx.room.Ignore
|
||||||
import androidx.room.Relation
|
import androidx.room.Relation
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
|
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.search.Searchable
|
||||||
|
|
||||||
class MessageFull(
|
class MessageFull(
|
||||||
profileId: Int, id: Long, type: Int,
|
profileId: Int, id: Long, type: Int,
|
||||||
@ -16,7 +18,7 @@ class MessageFull(
|
|||||||
profileId, id, type,
|
profileId, id, type,
|
||||||
subject, body, senderId,
|
subject, body, senderId,
|
||||||
addedDate
|
addedDate
|
||||||
) {
|
), Searchable<MessageFull> {
|
||||||
var senderName: String? = null
|
var senderName: String? = null
|
||||||
@Relation(parentColumn = "messageId", entityColumn = "messageId", entity = MessageRecipient::class)
|
@Relation(parentColumn = "messageId", entityColumn = "messageId", entity = MessageRecipient::class)
|
||||||
var recipients: MutableList<MessageRecipientFull>? = null
|
var recipients: MutableList<MessageRecipientFull>? = null
|
||||||
@ -27,11 +29,56 @@ class MessageFull(
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@delegate:Ignore
|
||||||
|
@delegate:Transient
|
||||||
|
val bodyHtml by lazy {
|
||||||
|
body?.let {
|
||||||
|
HtmlCompat.fromHtml(it, HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Ignore
|
@Ignore
|
||||||
var filterWeight = 0
|
@Transient
|
||||||
|
override var searchPriority = 0
|
||||||
|
|
||||||
@Ignore
|
@Ignore
|
||||||
var searchHighlightText: CharSequence? = null
|
@Transient
|
||||||
|
override var searchHighlightText: String? = null
|
||||||
|
|
||||||
|
@delegate:Ignore
|
||||||
|
@delegate:Transient
|
||||||
|
override val searchKeywords by lazy {
|
||||||
|
listOf(
|
||||||
|
when {
|
||||||
|
isSent -> recipients?.map { it.fullName }
|
||||||
|
else -> listOf(senderName)
|
||||||
|
},
|
||||||
|
listOf(subject),
|
||||||
|
listOf(bodyHtml?.toString()),
|
||||||
|
attachmentNames,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun compareTo(other: Searchable<*>): Int {
|
||||||
|
if (other !is MessageFull)
|
||||||
|
return 0
|
||||||
|
return when {
|
||||||
|
// ascending sorting
|
||||||
|
searchPriority > other.searchPriority -> 1
|
||||||
|
searchPriority < other.searchPriority -> -1
|
||||||
|
// descending sorting (1. true, 2. false)
|
||||||
|
isStarred && !other.isStarred -> -1
|
||||||
|
!isStarred && other.isStarred -> 1
|
||||||
|
// descending sorting
|
||||||
|
addedDate > other.addedDate -> -1
|
||||||
|
addedDate < other.addedDate -> 1
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Ignore
|
@Ignore
|
||||||
|
@Transient
|
||||||
var readByEveryone = true
|
var readByEveryone = true
|
||||||
|
|
||||||
// metadata
|
// metadata
|
||||||
|
@ -14,15 +14,15 @@ import kotlinx.coroutines.*
|
|||||||
import pl.szczodrzynski.edziennik.*
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
|
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
|
||||||
import pl.szczodrzynski.edziennik.databinding.DialogDayBinding
|
import pl.szczodrzynski.edziennik.databinding.DialogDayBinding
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventDetailsDialog
|
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListAdapter
|
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
|
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.lessonchange.LessonChangeDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.lessonchange.LessonChangeDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.teacherabsence.TeacherAbsenceDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.teacherabsence.TeacherAbsenceDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchanges.LessonChangesEvent
|
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchanges.LessonChangesEvent
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchanges.LessonChangesEventRenderer
|
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchanges.LessonChangesEventRenderer
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEvent
|
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEvent
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEventRenderer
|
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEventRenderer
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.event.EventDetailsDialog
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.event.EventListAdapter
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.event.EventManualDialog
|
||||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||||
@ -161,7 +161,7 @@ class DayDialog(
|
|||||||
b.teacherAbsenceFrame.isVisible = teacherAbsences.isNotEmpty()
|
b.teacherAbsenceFrame.isVisible = teacherAbsences.isNotEmpty()
|
||||||
|
|
||||||
adapter = EventListAdapter(
|
adapter = EventListAdapter(
|
||||||
activity,
|
activity = activity,
|
||||||
showWeekDay = false,
|
showWeekDay = false,
|
||||||
showDate = false,
|
showDate = false,
|
||||||
showType = true,
|
showType = true,
|
||||||
@ -188,10 +188,12 @@ class DayDialog(
|
|||||||
)
|
)
|
||||||
|
|
||||||
app.db.eventDao().getAllByDate(profileId, date).observe(activity) { events ->
|
app.db.eventDao().getAllByDate(profileId, date).observe(activity) { events ->
|
||||||
adapter.items = if (eventTypeId != null)
|
adapter.setAllItems(
|
||||||
events.filter { it.type == eventTypeId }
|
if (eventTypeId != null)
|
||||||
else
|
events.filter { it.type == eventTypeId }
|
||||||
events
|
else
|
||||||
|
events,
|
||||||
|
)
|
||||||
|
|
||||||
if (b.eventsView.adapter == null) {
|
if (b.eventsView.adapter == null) {
|
||||||
b.eventsView.adapter = adapter
|
b.eventsView.adapter = adapter
|
||||||
|
@ -1,126 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Kacper Ziubryniewicz 2019-11-30
|
|
||||||
*/
|
|
||||||
|
|
||||||
package pl.szczodrzynski.edziennik.ui.dialogs.event
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import pl.szczodrzynski.edziennik.*
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
|
||||||
import pl.szczodrzynski.edziennik.databinding.EventListItemBinding
|
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
|
|
||||||
class EventListAdapter(
|
|
||||||
val context: Context,
|
|
||||||
val simpleMode: Boolean = false,
|
|
||||||
val showWeekDay: Boolean = false,
|
|
||||||
val showDate: Boolean = false,
|
|
||||||
val showType: Boolean = true,
|
|
||||||
val showTime: Boolean = true,
|
|
||||||
val showSubject: Boolean = true,
|
|
||||||
val markAsSeen: Boolean = true,
|
|
||||||
val onItemClick: ((event: EventFull) -> Unit)? = null,
|
|
||||||
val onEventEditClick: ((event: EventFull) -> Unit)? = null
|
|
||||||
) : RecyclerView.Adapter<EventListAdapter.ViewHolder>(), CoroutineScope {
|
|
||||||
|
|
||||||
private val app = context.applicationContext as App
|
|
||||||
private val manager
|
|
||||||
get() = app.eventManager
|
|
||||||
|
|
||||||
private val job = Job()
|
|
||||||
override val coroutineContext: CoroutineContext
|
|
||||||
get() = job + Dispatchers.Main
|
|
||||||
|
|
||||||
var items = listOf<EventFull>()
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
|
||||||
val inflater = LayoutInflater.from(parent.context)
|
|
||||||
val view = EventListItemBinding.inflate(inflater, parent, false)
|
|
||||||
return ViewHolder(view)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
|
||||||
val event = items[position]
|
|
||||||
val b = holder.b
|
|
||||||
val manager = app.eventManager
|
|
||||||
|
|
||||||
b.root.onClick {
|
|
||||||
onItemClick?.invoke(event)
|
|
||||||
if (!event.seen) {
|
|
||||||
manager.markAsSeen(event)
|
|
||||||
}
|
|
||||||
if (event.showAsUnseen == true) {
|
|
||||||
event.showAsUnseen = false
|
|
||||||
notifyItemChanged(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val bullet = " • "
|
|
||||||
|
|
||||||
b.simpleMode = simpleMode
|
|
||||||
|
|
||||||
manager.setEventTopic(b.topic, event, showType = false)
|
|
||||||
b.topic.maxLines = if (simpleMode) 2 else 3
|
|
||||||
|
|
||||||
b.details.text = mutableListOf<CharSequence?>(
|
|
||||||
if (showWeekDay) Week.getFullDayName(event.date.weekDay) else null,
|
|
||||||
if (showDate) event.date.getRelativeString(context, 7) ?: event.date.formattedStringShort else null,
|
|
||||||
if (showType) event.typeName else null,
|
|
||||||
if (showTime) event.time?.stringHM ?: app.getString(R.string.event_all_day) else null,
|
|
||||||
if (showSubject) event.subjectLongName else null
|
|
||||||
).concat(bullet)
|
|
||||||
|
|
||||||
b.addedBy.setText(
|
|
||||||
when (event.sharedBy) {
|
|
||||||
null -> when {
|
|
||||||
event.addedManually -> R.string.event_list_added_by_self_format
|
|
||||||
event.teacherName == null -> R.string.event_list_added_by_unknown_format
|
|
||||||
else -> R.string.event_list_added_by_format
|
|
||||||
}
|
|
||||||
"self" -> R.string.event_list_shared_by_self_format
|
|
||||||
else -> R.string.event_list_shared_by_format
|
|
||||||
},
|
|
||||||
Date.fromMillis(event.addedDate).formattedString,
|
|
||||||
event.sharedByName ?: event.teacherName ?: "",
|
|
||||||
event.teamName?.let { bullet+it } ?: ""
|
|
||||||
)
|
|
||||||
|
|
||||||
b.typeColor.background?.setTintColor(event.eventColor)
|
|
||||||
b.typeColor.isVisible = showType
|
|
||||||
|
|
||||||
b.editButton.isVisible = !simpleMode && event.addedManually && !event.isDone
|
|
||||||
b.editButton.onClick {
|
|
||||||
onEventEditClick?.invoke(event)
|
|
||||||
}
|
|
||||||
b.editButton.attachToastHint(R.string.hint_edit_event)
|
|
||||||
|
|
||||||
if (event.showAsUnseen == null)
|
|
||||||
event.showAsUnseen = !event.seen
|
|
||||||
|
|
||||||
b.unread.isVisible = event.showAsUnseen == true
|
|
||||||
if (markAsSeen && !event.seen) {
|
|
||||||
manager.markAsSeen(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun notifyItemChanged(model: Any) {
|
|
||||||
startCoroutineTimer(1000L, 0L) {
|
|
||||||
val index = items.indexOf(model)
|
|
||||||
if (index != -1)
|
|
||||||
notifyItemChanged(index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemCount() = items.size
|
|
||||||
|
|
||||||
class ViewHolder(val b: EventListItemBinding) : RecyclerView.ViewHolder(b.root)
|
|
||||||
}
|
|
@ -14,7 +14,7 @@ import pl.szczodrzynski.edziennik.MainActivity
|
|||||||
import pl.szczodrzynski.edziennik.R
|
import pl.szczodrzynski.edziennik.R
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||||
import pl.szczodrzynski.edziennik.databinding.DialogLessonDetailsBinding
|
import pl.szczodrzynski.edziennik.databinding.DialogLessonDetailsBinding
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
|
import pl.szczodrzynski.edziennik.ui.modules.messages.list.MessagesFragment
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
class SyncViewListDialog(
|
class SyncViewListDialog(
|
||||||
|
@ -26,10 +26,10 @@ import pl.szczodrzynski.edziennik.data.db.full.LessonFull
|
|||||||
import pl.szczodrzynski.edziennik.databinding.DialogLessonDetailsBinding
|
import pl.szczodrzynski.edziennik.databinding.DialogLessonDetailsBinding
|
||||||
import pl.szczodrzynski.edziennik.onClick
|
import pl.szczodrzynski.edziennik.onClick
|
||||||
import pl.szczodrzynski.edziennik.setText
|
import pl.szczodrzynski.edziennik.setText
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventDetailsDialog
|
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListAdapter
|
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
|
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceDetailsDialog
|
import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceDetailsDialog
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.event.EventDetailsDialog
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.event.EventListAdapter
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.event.EventManualDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
|
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
|
||||||
import pl.szczodrzynski.edziennik.utils.BetterLink
|
import pl.szczodrzynski.edziennik.utils.BetterLink
|
||||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||||
@ -228,7 +228,7 @@ class LessonDetailsDialog(
|
|||||||
)
|
)
|
||||||
|
|
||||||
app.db.eventDao().getAllByDateTime(lesson.profileId, lessonDate, lessonTime).observe(activity, Observer { events ->
|
app.db.eventDao().getAllByDateTime(lesson.profileId, lessonDate, lessonTime).observe(activity, Observer { events ->
|
||||||
adapter.items = events
|
adapter.setAllItems(events)
|
||||||
if (b.eventsView.adapter == null) {
|
if (b.eventsView.adapter == null) {
|
||||||
b.eventsView.adapter = adapter
|
b.eventsView.adapter = adapter
|
||||||
b.eventsView.apply {
|
b.eventsView.apply {
|
||||||
|
@ -28,7 +28,7 @@ import pl.szczodrzynski.edziennik.databinding.FragmentAgendaCalendarBinding
|
|||||||
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaDefaultBinding
|
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaDefaultBinding
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.agenda.AgendaConfigDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.agenda.AgendaConfigDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.day.DayDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.day.DayDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
|
import pl.szczodrzynski.edziennik.ui.modules.event.EventManualDialog
|
||||||
import pl.szczodrzynski.edziennik.utils.Themes
|
import pl.szczodrzynski.edziennik.utils.Themes
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
||||||
|
@ -22,7 +22,6 @@ import pl.szczodrzynski.edziennik.MainActivity
|
|||||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||||
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaDefaultBinding
|
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaDefaultBinding
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.day.DayDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.day.DayDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventDetailsDialog
|
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.lessonchange.LessonChangeDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.lessonchange.LessonChangeDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.teacherabsence.TeacherAbsenceDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.teacherabsence.TeacherAbsenceDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.event.AgendaEvent
|
import pl.szczodrzynski.edziennik.ui.modules.agenda.event.AgendaEvent
|
||||||
@ -33,6 +32,7 @@ import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchanges.LessonChangesE
|
|||||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchanges.LessonChangesEventRenderer
|
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchanges.LessonChangesEventRenderer
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEvent
|
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEvent
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEventRenderer
|
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEventRenderer
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.event.EventDetailsDialog
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
* Copyright (c) Kuba Szczodrzyński 2019-12-18.
|
* Copyright (c) Kuba Szczodrzyński 2019-12-18.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pl.szczodrzynski.edziennik.ui.dialogs.event
|
package pl.szczodrzynski.edziennik.ui.modules.event
|
||||||
|
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.Intent
|
import android.content.Intent
|
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kacper Ziubryniewicz 2019-11-30
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.modules.event
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import pl.szczodrzynski.edziennik.App
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||||
|
import pl.szczodrzynski.edziennik.startCoroutineTimer
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.search.SearchableAdapter
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
class EventListAdapter(
|
||||||
|
val activity: AppCompatActivity,
|
||||||
|
val simpleMode: Boolean = false,
|
||||||
|
val showWeekDay: Boolean = false,
|
||||||
|
val showDate: Boolean = false,
|
||||||
|
val showType: Boolean = true,
|
||||||
|
val showTime: Boolean = true,
|
||||||
|
val showSubject: Boolean = true,
|
||||||
|
val markAsSeen: Boolean = true,
|
||||||
|
isReversed: Boolean = false,
|
||||||
|
val onItemClick: ((event: EventFull) -> Unit)? = null,
|
||||||
|
val onEventEditClick: ((event: EventFull) -> Unit)? = null,
|
||||||
|
) : SearchableAdapter<EventFull>(isReversed), CoroutineScope {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "EventListAdapter"
|
||||||
|
private const val ITEM_TYPE_EVENT = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private val app = activity.applicationContext as App
|
||||||
|
private val manager
|
||||||
|
get() = app.eventManager
|
||||||
|
|
||||||
|
private val job = Job()
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
|
override fun getItemViewType(item: EventFull) = ITEM_TYPE_EVENT
|
||||||
|
|
||||||
|
override fun onBindViewHolder(
|
||||||
|
holder: RecyclerView.ViewHolder,
|
||||||
|
position: Int,
|
||||||
|
item: EventFull,
|
||||||
|
) {
|
||||||
|
if (holder !is EventViewHolder)
|
||||||
|
return
|
||||||
|
holder.onBind(activity, app, item, position, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
parent: ViewGroup,
|
||||||
|
viewType: Int,
|
||||||
|
) = EventViewHolder(inflater, parent)
|
||||||
|
|
||||||
|
internal fun notifyItemChanged(model: Any) {
|
||||||
|
startCoroutineTimer(1000L, 0L) {
|
||||||
|
val index = items.indexOf(model)
|
||||||
|
if (index != -1)
|
||||||
|
notifyItemChanged(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
* Copyright (c) Kuba Szczodrzyński 2019-11-12.
|
* Copyright (c) Kuba Szczodrzyński 2019-11-12.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pl.szczodrzynski.edziennik.ui.dialogs.event
|
package pl.szczodrzynski.edziennik.ui.modules.event
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2021-10-10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.modules.event
|
||||||
|
|
||||||
|
import android.text.SpannableString
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.mikepenz.iconics.utils.buildIconics
|
||||||
|
import pl.szczodrzynski.edziennik.*
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||||
|
import pl.szczodrzynski.edziennik.databinding.EventListItemBinding
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||||
|
|
||||||
|
class EventViewHolder(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
parent: ViewGroup,
|
||||||
|
val b: EventListItemBinding = EventListItemBinding.inflate(inflater, parent, false),
|
||||||
|
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<EventFull, EventListAdapter> {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "EventViewHolder"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBind(
|
||||||
|
activity: AppCompatActivity,
|
||||||
|
app: App,
|
||||||
|
item: EventFull,
|
||||||
|
position: Int,
|
||||||
|
adapter: EventListAdapter,
|
||||||
|
) {
|
||||||
|
val manager = app.eventManager
|
||||||
|
|
||||||
|
b.root.onClick {
|
||||||
|
adapter.onItemClick?.invoke(item)
|
||||||
|
if (!item.seen) {
|
||||||
|
manager.markAsSeen(item)
|
||||||
|
}
|
||||||
|
if (item.showAsUnseen == true) {
|
||||||
|
item.showAsUnseen = false
|
||||||
|
adapter.notifyItemChanged(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val bullet = " • "
|
||||||
|
val colorHighlight = R.attr.colorControlHighlight.resolveAttr(activity)
|
||||||
|
|
||||||
|
b.simpleMode = adapter.simpleMode
|
||||||
|
|
||||||
|
manager.setEventTopic(b.topic, item, showType = false)
|
||||||
|
b.topic.text = SpannableString(
|
||||||
|
adapter.highlightSearchText(
|
||||||
|
item = item,
|
||||||
|
text = b.topic.text,
|
||||||
|
color = colorHighlight
|
||||||
|
)
|
||||||
|
).buildIconics()
|
||||||
|
b.topic.maxLines = if (adapter.simpleMode) 2 else 3
|
||||||
|
|
||||||
|
b.details.text = mutableListOf(
|
||||||
|
if (adapter.showWeekDay)
|
||||||
|
Week.getFullDayName(item.date.weekDay)
|
||||||
|
else null,
|
||||||
|
if (adapter.showDate)
|
||||||
|
item.date.getRelativeString(activity, 7) ?: item.date.formattedStringShort
|
||||||
|
else null,
|
||||||
|
if (adapter.showType)
|
||||||
|
item.typeName
|
||||||
|
else null,
|
||||||
|
if (adapter.showTime)
|
||||||
|
item.time?.stringHM ?: app.getString(R.string.event_all_day)
|
||||||
|
else null,
|
||||||
|
if (adapter.showSubject)
|
||||||
|
adapter.highlightSearchText(
|
||||||
|
item = item,
|
||||||
|
text = item.subjectLongName ?: "",
|
||||||
|
color = colorHighlight
|
||||||
|
)
|
||||||
|
else null,
|
||||||
|
).concat(bullet)
|
||||||
|
|
||||||
|
val addedBy = item.sharedByName ?: item.teacherName ?: ""
|
||||||
|
b.addedBy.setText(
|
||||||
|
when (item.sharedBy) {
|
||||||
|
null -> when {
|
||||||
|
item.addedManually -> R.string.event_list_added_by_self_format
|
||||||
|
item.teacherName == null -> R.string.event_list_added_by_unknown_format
|
||||||
|
else -> R.string.event_list_added_by_format
|
||||||
|
}
|
||||||
|
"self" -> R.string.event_list_shared_by_self_format
|
||||||
|
else -> R.string.event_list_shared_by_format
|
||||||
|
},
|
||||||
|
/* 1$ */
|
||||||
|
Date.fromMillis(item.addedDate).formattedString,
|
||||||
|
/* 2$ */
|
||||||
|
addedBy,
|
||||||
|
/* 3$ */
|
||||||
|
item.teamName?.let { bullet + it } ?: "",
|
||||||
|
)
|
||||||
|
val addedBySpanned = adapter.highlightSearchText(
|
||||||
|
item = item,
|
||||||
|
text = addedBy,
|
||||||
|
color = colorHighlight
|
||||||
|
)
|
||||||
|
b.addedBy.text = SpannableString(
|
||||||
|
b.addedBy.text.replace(addedBy, addedBySpanned)
|
||||||
|
).buildIconics()
|
||||||
|
|
||||||
|
b.attachmentIcon.isVisible = item.hasAttachments
|
||||||
|
|
||||||
|
b.typeColor.background?.setTintColor(item.eventColor)
|
||||||
|
b.typeColor.isVisible = adapter.showType
|
||||||
|
|
||||||
|
b.editButton.isVisible = !adapter.simpleMode && item.addedManually && !item.isDone
|
||||||
|
b.editButton.onClick {
|
||||||
|
adapter.onEventEditClick?.invoke(item)
|
||||||
|
}
|
||||||
|
b.editButton.attachToastHint(R.string.hint_edit_event)
|
||||||
|
|
||||||
|
if (item.showAsUnseen == null)
|
||||||
|
item.showAsUnseen = !item.seen
|
||||||
|
|
||||||
|
b.unread.isVisible = item.showAsUnseen == true
|
||||||
|
if (adapter.markAsSeen && !item.seen) {
|
||||||
|
manager.markAsSeen(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,9 +22,9 @@ import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
|||||||
import pl.szczodrzynski.edziennik.databinding.CardHomeEventsBinding
|
import pl.szczodrzynski.edziennik.databinding.CardHomeEventsBinding
|
||||||
import pl.szczodrzynski.edziennik.dp
|
import pl.szczodrzynski.edziennik.dp
|
||||||
import pl.szczodrzynski.edziennik.onClick
|
import pl.szczodrzynski.edziennik.onClick
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventDetailsDialog
|
import pl.szczodrzynski.edziennik.ui.modules.event.EventDetailsDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListAdapter
|
import pl.szczodrzynski.edziennik.ui.modules.event.EventListAdapter
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
|
import pl.szczodrzynski.edziennik.ui.modules.event.EventManualDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCard
|
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCard
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardAdapter
|
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardAdapter
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
|
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
|
||||||
@ -82,7 +82,7 @@ class HomeEventsCard(
|
|||||||
)
|
)
|
||||||
|
|
||||||
app.db.eventDao().getNearestNotDone(profile.id, Date.getToday(), 4).observe(activity, Observer { events ->
|
app.db.eventDao().getNearestNotDone(profile.id, Date.getToday(), 4).observe(activity, Observer { events ->
|
||||||
adapter.items = events
|
adapter.setAllItems(events)
|
||||||
if (b.eventsView.adapter == null) {
|
if (b.eventsView.adapter == null) {
|
||||||
b.eventsView.adapter = adapter
|
b.eventsView.adapter = adapter
|
||||||
b.eventsView.apply {
|
b.eventsView.apply {
|
||||||
|
@ -20,8 +20,8 @@ import pl.szczodrzynski.edziennik.*
|
|||||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||||
import pl.szczodrzynski.edziennik.databinding.HomeworkFragmentBinding
|
import pl.szczodrzynski.edziennik.databinding.HomeworkFragmentBinding
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
|
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.FragmentLazyPagerAdapter
|
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.FragmentLazyPagerAdapter
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.event.EventManualDialog
|
||||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
||||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
@ -13,10 +13,10 @@ import kotlinx.coroutines.Job
|
|||||||
import pl.szczodrzynski.edziennik.*
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||||
import pl.szczodrzynski.edziennik.databinding.HomeworkListFragmentBinding
|
import pl.szczodrzynski.edziennik.databinding.HomeworkListFragmentBinding
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventDetailsDialog
|
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListAdapter
|
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
|
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
|
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.event.EventDetailsDialog
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.event.EventListAdapter
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.event.EventManualDialog
|
||||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
@ -54,57 +54,57 @@ class HomeworkListFragment : LazyFragment(), CoroutineScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val adapter = EventListAdapter(
|
val adapter = EventListAdapter(
|
||||||
activity,
|
activity,
|
||||||
showWeekDay = true,
|
showWeekDay = true,
|
||||||
showDate = true,
|
showDate = true,
|
||||||
showType = false,
|
showType = false,
|
||||||
showTime = true,
|
showTime = true,
|
||||||
showSubject = true,
|
showSubject = true,
|
||||||
markAsSeen = true,
|
markAsSeen = true,
|
||||||
onItemClick = {
|
isReversed = homeworkDate == HomeworkDate.PAST,
|
||||||
EventDetailsDialog(
|
onItemClick = {
|
||||||
activity,
|
EventDetailsDialog(
|
||||||
it
|
activity,
|
||||||
)
|
it
|
||||||
},
|
)
|
||||||
onEventEditClick = {
|
},
|
||||||
EventManualDialog(
|
onEventEditClick = {
|
||||||
activity,
|
EventManualDialog(
|
||||||
it.profileId,
|
activity,
|
||||||
editingEvent = it
|
it.profileId,
|
||||||
)
|
editingEvent = it
|
||||||
}
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
app.db.eventDao().getAllByType(App.profileId, Event.TYPE_HOMEWORK, filter).observe(this@HomeworkListFragment, Observer { items ->
|
app.db.eventDao().getAllByType(App.profileId, Event.TYPE_HOMEWORK, filter).observe(this@HomeworkListFragment, Observer { events ->
|
||||||
if (!isAdded) return@Observer
|
if (!isAdded) return@Observer
|
||||||
|
|
||||||
// load & configure the adapter
|
// show/hide relevant views
|
||||||
adapter.items = items
|
setSwipeToRefresh(events.isEmpty())
|
||||||
if (items.isNotNullNorEmpty() && b.list.adapter == null) {
|
b.progressBar.isVisible = false
|
||||||
b.list.adapter = adapter
|
b.list.isVisible = events.isNotEmpty()
|
||||||
|
b.noData.isVisible = events.isEmpty()
|
||||||
|
if (events.isEmpty()) {
|
||||||
|
return@Observer
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply the new event list
|
||||||
|
adapter.setAllItems(events, addSearchField = true)
|
||||||
|
|
||||||
|
// configure the adapter & recycler view
|
||||||
|
if (b.list.adapter == null) {
|
||||||
b.list.apply {
|
b.list.apply {
|
||||||
setHasFixedSize(true)
|
setHasFixedSize(true)
|
||||||
layoutManager = LinearLayoutManager(context).apply {
|
layoutManager = LinearLayoutManager(context)
|
||||||
reverseLayout = homeworkDate == HomeworkDate.PAST
|
|
||||||
stackFromEnd = homeworkDate == HomeworkDate.PAST
|
|
||||||
}
|
|
||||||
addItemDecoration(SimpleDividerItemDecoration(context))
|
addItemDecoration(SimpleDividerItemDecoration(context))
|
||||||
addOnScrollListener(onScrollListener)
|
addOnScrollListener(onScrollListener)
|
||||||
|
this.adapter = adapter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
adapter.notifyDataSetChanged()
|
|
||||||
setSwipeToRefresh(items.isNullOrEmpty())
|
|
||||||
|
|
||||||
// show/hide relevant views
|
// reapply the filter
|
||||||
b.progressBar.isVisible = false
|
adapter.getSearchField()?.applyTo(adapter)
|
||||||
if (items.isNullOrEmpty()) {
|
|
||||||
b.list.isVisible = false
|
|
||||||
b.noData.isVisible = true
|
|
||||||
} else {
|
|
||||||
b.list.isVisible = true
|
|
||||||
b.noData.isVisible = false
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}; return true }
|
}; return true }
|
||||||
}
|
}
|
||||||
|
@ -1,86 +0,0 @@
|
|||||||
package pl.szczodrzynski.edziennik.ui.modules.messages
|
|
||||||
|
|
||||||
import android.graphics.Typeface
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.Filterable
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import pl.szczodrzynski.edziennik.App
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
|
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.models.MessagesSearch
|
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.utils.MessagesFilter
|
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.viewholder.MessageViewHolder
|
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.viewholder.SearchViewHolder
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
|
|
||||||
class MessagesAdapter(
|
|
||||||
val activity: AppCompatActivity,
|
|
||||||
val teachers: List<Teacher>,
|
|
||||||
val onItemClick: ((item: MessageFull) -> Unit)? = null,
|
|
||||||
val onStarClick: ((item: MessageFull) -> Unit)? = null,
|
|
||||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), CoroutineScope, Filterable {
|
|
||||||
companion object {
|
|
||||||
private const val TAG = "MessagesAdapter"
|
|
||||||
private const val ITEM_TYPE_MESSAGE = 0
|
|
||||||
private const val ITEM_TYPE_SEARCH = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
private val app = activity.applicationContext as App
|
|
||||||
// optional: place the manager here
|
|
||||||
internal val manager
|
|
||||||
get() = app.messageManager
|
|
||||||
|
|
||||||
private val job = Job()
|
|
||||||
override val coroutineContext: CoroutineContext
|
|
||||||
get() = job + Dispatchers.Main
|
|
||||||
|
|
||||||
// mutable var changed by the filter
|
|
||||||
var items = listOf<Any>()
|
|
||||||
// mutable list managed by the fragment
|
|
||||||
val allItems = mutableListOf<Any>()
|
|
||||||
val typefaceNormal: Typeface by lazy { Typeface.create(Typeface.DEFAULT, Typeface.NORMAL) }
|
|
||||||
val typefaceBold: Typeface by lazy { Typeface.create(Typeface.DEFAULT, Typeface.BOLD) }
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
|
||||||
val inflater = LayoutInflater.from(parent.context)
|
|
||||||
return when (viewType) {
|
|
||||||
ITEM_TYPE_MESSAGE -> MessageViewHolder(inflater, parent)
|
|
||||||
ITEM_TYPE_SEARCH -> SearchViewHolder(inflater, parent)
|
|
||||||
else -> throw IllegalArgumentException("Incorrect viewType")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int {
|
|
||||||
return when (items[position]) {
|
|
||||||
is MessageFull -> ITEM_TYPE_MESSAGE
|
|
||||||
is MessagesSearch -> ITEM_TYPE_SEARCH
|
|
||||||
else -> throw IllegalArgumentException("Incorrect viewType")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
|
||||||
val item = items[position]
|
|
||||||
if (holder !is BindableViewHolder<*, *>)
|
|
||||||
return
|
|
||||||
|
|
||||||
when {
|
|
||||||
holder is MessageViewHolder
|
|
||||||
&& item is MessageFull -> holder.onBind(activity, app, item, position, this)
|
|
||||||
holder is SearchViewHolder
|
|
||||||
&& item is MessagesSearch -> holder.onBind(activity, app, item, position, this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val messagesFilter by lazy {
|
|
||||||
MessagesFilter(this)
|
|
||||||
}
|
|
||||||
override fun getItemCount() = items.size
|
|
||||||
override fun getFilter() = messagesFilter
|
|
||||||
}
|
|
@ -9,7 +9,6 @@ import android.text.Spanned
|
|||||||
import androidx.core.graphics.ColorUtils
|
import androidx.core.graphics.ColorUtils
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.App
|
||||||
import pl.szczodrzynski.edziennik.R
|
import pl.szczodrzynski.edziennik.R
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||||
import pl.szczodrzynski.edziennik.fixName
|
import pl.szczodrzynski.edziennik.fixName
|
||||||
import pl.szczodrzynski.edziennik.getNameInitials
|
import pl.szczodrzynski.edziennik.getNameInitials
|
||||||
@ -123,10 +122,10 @@ object MessagesUtils {
|
|||||||
fun getMessageInfo(app: App, message: MessageFull, diameterDp: Int, textSizeBigDp: Int, textSizeMediumDp: Int, textSizeSmallDp: Int): MessageInfo {
|
fun getMessageInfo(app: App, message: MessageFull, diameterDp: Int, textSizeBigDp: Int, textSizeMediumDp: Int, textSizeSmallDp: Int): MessageInfo {
|
||||||
var profileImage: Bitmap? = null
|
var profileImage: Bitmap? = null
|
||||||
var profileName: String? = null
|
var profileName: String? = null
|
||||||
if (message.type == Message.TYPE_RECEIVED || message.type == Message.TYPE_DELETED) {
|
if (message.isReceived || message.isDeleted) {
|
||||||
profileName = message.senderName?.fixName()
|
profileName = message.senderName?.fixName()
|
||||||
profileImage = getProfileImage(diameterDp, textSizeBigDp, textSizeMediumDp, textSizeSmallDp, 1, profileName)
|
profileImage = getProfileImage(diameterDp, textSizeBigDp, textSizeMediumDp, textSizeSmallDp, 1, profileName)
|
||||||
} else if (message.type == Message.TYPE_SENT || message.type == Message.TYPE_DRAFT && message.recipients != null) {
|
} else if (message.isSent || message.isDraft && message.recipients != null) {
|
||||||
when (val count = message.recipients?.size ?: 0) {
|
when (val count = message.recipients?.size ?: 0) {
|
||||||
0 -> {
|
0 -> {
|
||||||
profileName = app.getString(R.string.messages_draft_title)
|
profileName = app.getString(R.string.messages_draft_title)
|
||||||
|
@ -37,7 +37,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Message
|
|||||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||||
import pl.szczodrzynski.edziennik.databinding.MessagesComposeFragmentBinding
|
import pl.szczodrzynski.edziennik.databinding.MessagesComposeFragmentBinding
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.MessagesConfigDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.MessagesConfigDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
|
import pl.szczodrzynski.edziennik.ui.modules.messages.list.MessagesFragment
|
||||||
import pl.szczodrzynski.edziennik.utils.Themes
|
import pl.szczodrzynski.edziennik.utils.Themes
|
||||||
import pl.szczodrzynski.edziennik.utils.managers.MessageManager.UIConfig
|
import pl.szczodrzynski.edziennik.utils.managers.MessageManager.UIConfig
|
||||||
import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.StylingConfig
|
import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.StylingConfig
|
||||||
@ -398,7 +398,7 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
|
|||||||
b.recipients.setAdapter(adapter)
|
b.recipients.setAdapter(adapter)
|
||||||
|
|
||||||
val message = manager.fillWithBundle(uiConfig, arguments)
|
val message = manager.fillWithBundle(uiConfig, arguments)
|
||||||
if (message != null && message.type == Message.TYPE_DRAFT) {
|
if (message != null && message.isDraft) {
|
||||||
draftMessageId = message.id
|
draftMessageId = message.id
|
||||||
if (discardDraftItem != null)
|
if (discardDraftItem != null)
|
||||||
activity.bottomSheet.addItemAt(2, discardDraftItem!!)
|
activity.bottomSheet.addItemAt(2, discardDraftItem!!)
|
||||||
|
@ -2,29 +2,24 @@
|
|||||||
* Copyright (c) Kuba Szczodrzyński 2020-4-5.
|
* Copyright (c) Kuba Szczodrzyński 2020-4-5.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pl.szczodrzynski.edziennik.ui.modules.messages.viewholder
|
package pl.szczodrzynski.edziennik.ui.modules.messages.list
|
||||||
|
|
||||||
import android.graphics.Typeface
|
|
||||||
import android.text.style.BackgroundColorSpan
|
|
||||||
import android.text.style.StyleSpan
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import pl.szczodrzynski.edziennik.*
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||||
import pl.szczodrzynski.edziennik.databinding.MessagesListItemBinding
|
import pl.szczodrzynski.edziennik.databinding.MessagesListItemBinding
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
|
import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesAdapter
|
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesUtils
|
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesUtils
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
|
|
||||||
class MessageViewHolder(
|
class MessageViewHolder(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
parent: ViewGroup,
|
parent: ViewGroup,
|
||||||
val b: MessagesListItemBinding = MessagesListItemBinding.inflate(inflater, parent, false)
|
val b: MessagesListItemBinding = MessagesListItemBinding.inflate(inflater, parent, false),
|
||||||
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<MessageFull, MessagesAdapter> {
|
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<MessageFull, MessagesAdapter> {
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "MessageViewHolder"
|
private const val TAG = "MessageViewHolder"
|
||||||
@ -35,16 +30,14 @@ class MessageViewHolder(
|
|||||||
app: App,
|
app: App,
|
||||||
item: MessageFull,
|
item: MessageFull,
|
||||||
position: Int,
|
position: Int,
|
||||||
adapter: MessagesAdapter
|
adapter: MessagesAdapter,
|
||||||
) {
|
) {
|
||||||
b.messageSubject.text = item.subject
|
|
||||||
b.messageDate.text = Date.fromMillis(item.addedDate).formattedStringShort
|
b.messageDate.text = Date.fromMillis(item.addedDate).formattedStringShort
|
||||||
b.messageAttachmentImage.isVisible = item.hasAttachments
|
b.messageAttachmentImage.isVisible = item.hasAttachments
|
||||||
|
|
||||||
val text = item.body?.take(200) ?: ""
|
b.messageBody.text = item.bodyHtml?.take(200)
|
||||||
b.messageBody.text = MessagesUtils.htmlToSpannable(activity, text)
|
|
||||||
|
|
||||||
val isRead = item.type == Message.TYPE_SENT || item.type == Message.TYPE_DRAFT || item.seen
|
val isRead = item.isSent || item.isDraft || item.seen
|
||||||
val typeface = if (isRead) adapter.typefaceNormal else adapter.typefaceBold
|
val typeface = if (isRead) adapter.typefaceNormal else adapter.typefaceBold
|
||||||
val style = if (isRead) R.style.NavView_TextView_Small else R.style.NavView_TextView_Normal
|
val style = if (isRead) R.style.NavView_TextView_Small else R.style.NavView_TextView_Normal
|
||||||
// set text styles
|
// set text styles
|
||||||
@ -62,20 +55,18 @@ class MessageViewHolder(
|
|||||||
|
|
||||||
val messageInfo = MessagesUtils.getMessageInfo(app, item, 48, 24, 18, 12)
|
val messageInfo = MessagesUtils.getMessageInfo(app, item, 48, 24, 18, 12)
|
||||||
b.messageProfileBackground.setImageBitmap(messageInfo.profileImage)
|
b.messageProfileBackground.setImageBitmap(messageInfo.profileImage)
|
||||||
b.messageSender.text = messageInfo.profileName
|
|
||||||
|
|
||||||
item.searchHighlightText?.toString()?.let { highlight ->
|
val colorHighlight = R.attr.colorControlHighlight.resolveAttr(activity)
|
||||||
val colorHighlight = R.attr.colorControlHighlight.resolveAttr(activity)
|
b.messageSubject.text = adapter.highlightSearchText(
|
||||||
|
item = item,
|
||||||
b.messageSubject.text = b.messageSubject.text.asSpannable(
|
text = item.subject,
|
||||||
StyleSpan(Typeface.BOLD), BackgroundColorSpan(colorHighlight),
|
color = colorHighlight
|
||||||
substring = highlight, ignoreCase = true, ignoreDiacritics = true
|
)
|
||||||
)
|
b.messageSender.text = adapter.highlightSearchText(
|
||||||
b.messageSender.text = b.messageSender.text.asSpannable(
|
item = item,
|
||||||
StyleSpan(Typeface.BOLD), BackgroundColorSpan(colorHighlight),
|
text = messageInfo.profileName ?: "",
|
||||||
substring = highlight, ignoreCase = true, ignoreDiacritics = true
|
color = colorHighlight
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
adapter.onItemClick?.let { listener ->
|
adapter.onItemClick?.let { listener ->
|
||||||
b.root.onClick { listener(item) }
|
b.root.onClick { listener(item) }
|
@ -0,0 +1,50 @@
|
|||||||
|
package pl.szczodrzynski.edziennik.ui.modules.messages.list
|
||||||
|
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import pl.szczodrzynski.edziennik.App
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.search.SearchableAdapter
|
||||||
|
|
||||||
|
class MessagesAdapter(
|
||||||
|
val activity: AppCompatActivity,
|
||||||
|
val teachers: List<Teacher>,
|
||||||
|
val onItemClick: ((item: MessageFull) -> Unit)? = null,
|
||||||
|
val onStarClick: ((item: MessageFull) -> Unit)? = null,
|
||||||
|
) : SearchableAdapter<MessageFull>() {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "MessagesAdapter"
|
||||||
|
private const val ITEM_TYPE_MESSAGE = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private val app = activity.applicationContext as App
|
||||||
|
|
||||||
|
// optional: place the manager here
|
||||||
|
internal val manager
|
||||||
|
get() = app.messageManager
|
||||||
|
|
||||||
|
val typefaceNormal: Typeface by lazy { Typeface.create(Typeface.DEFAULT, Typeface.NORMAL) }
|
||||||
|
val typefaceBold: Typeface by lazy { Typeface.create(Typeface.DEFAULT, Typeface.BOLD) }
|
||||||
|
|
||||||
|
override fun getItemViewType(item: MessageFull) = ITEM_TYPE_MESSAGE
|
||||||
|
|
||||||
|
override fun onBindViewHolder(
|
||||||
|
holder: RecyclerView.ViewHolder,
|
||||||
|
position: Int,
|
||||||
|
item: MessageFull,
|
||||||
|
) {
|
||||||
|
if (holder !is MessageViewHolder)
|
||||||
|
return
|
||||||
|
holder.onBind(activity, app, item, position, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
parent: ViewGroup,
|
||||||
|
viewType: Int,
|
||||||
|
) = MessageViewHolder(inflater, parent)
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package pl.szczodrzynski.edziennik.ui.modules.messages
|
package pl.szczodrzynski.edziennik.ui.modules.messages.list
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
@ -2,7 +2,7 @@
|
|||||||
* Copyright (c) Kuba Szczodrzyński 2020-4-4.
|
* Copyright (c) Kuba Szczodrzyński 2020-4-4.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pl.szczodrzynski.edziennik.ui.modules.messages
|
package pl.szczodrzynski.edziennik.ui.modules.messages.list
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -17,10 +17,8 @@ import pl.szczodrzynski.edziennik.MainActivity.Companion.TARGET_MESSAGES_COMPOSE
|
|||||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.TARGET_MESSAGES_DETAILS
|
import pl.szczodrzynski.edziennik.MainActivity.Companion.TARGET_MESSAGES_DETAILS
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
|
||||||
import pl.szczodrzynski.edziennik.databinding.MessagesListFragmentBinding
|
import pl.szczodrzynski.edziennik.databinding.MessagesListFragmentBinding
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
|
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.models.MessagesSearch
|
|
||||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
@ -63,7 +61,7 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
|
|||||||
|
|
||||||
adapter = MessagesAdapter(activity, teachers, onItemClick = {
|
adapter = MessagesAdapter(activity, teachers, onItemClick = {
|
||||||
val (target, args) =
|
val (target, args) =
|
||||||
if (it.type == Message.TYPE_DRAFT) {
|
if (it.isDraft) {
|
||||||
TARGET_MESSAGES_COMPOSE to Bundle("message" to app.gson.toJson(it))
|
TARGET_MESSAGES_COMPOSE to Bundle("message" to app.gson.toJson(it))
|
||||||
} else {
|
} else {
|
||||||
TARGET_MESSAGES_DETAILS to Bundle("messageId" to it.id)
|
TARGET_MESSAGES_DETAILS to Bundle("messageId" to it.id)
|
||||||
@ -98,17 +96,8 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
|
|||||||
return@Observer
|
return@Observer
|
||||||
}
|
}
|
||||||
|
|
||||||
if (adapter.allItems.isEmpty()) {
|
// apply the new message list
|
||||||
// items empty - add the search field
|
adapter.setAllItems(messages, searchText, addSearchField = true)
|
||||||
adapter.allItems += MessagesSearch().also {
|
|
||||||
it.searchText = searchText ?: ""
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// items not empty - remove all messages
|
|
||||||
adapter.allItems.removeAll { it is MessageFull }
|
|
||||||
}
|
|
||||||
// add all messages
|
|
||||||
adapter.allItems.addAll(messages)
|
|
||||||
|
|
||||||
// configure the adapter & recycler view
|
// configure the adapter & recycler view
|
||||||
if (b.list.adapter == null) {
|
if (b.list.adapter == null) {
|
||||||
@ -125,8 +114,7 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
|
|||||||
val layoutManager = (b.list.layoutManager as? LinearLayoutManager) ?: return@Observer
|
val layoutManager = (b.list.layoutManager as? LinearLayoutManager) ?: return@Observer
|
||||||
|
|
||||||
// reapply the filter
|
// reapply the filter
|
||||||
val searchItem = adapter.items.firstOrNull { it is MessagesSearch } as? MessagesSearch
|
adapter.getSearchField()?.applyTo(adapter) {
|
||||||
adapter.filter.filter(searchText ?: searchItem?.searchText) {
|
|
||||||
// restore the previously saved scroll position
|
// restore the previously saved scroll position
|
||||||
recyclerViewState?.let {
|
recyclerViewState?.let {
|
||||||
layoutManager.onRestoreInstanceState(it)
|
layoutManager.onRestoreInstanceState(it)
|
||||||
@ -141,11 +129,11 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
|
|||||||
if (!isAdded || !this::adapter.isInitialized)
|
if (!isAdded || !this::adapter.isInitialized)
|
||||||
return
|
return
|
||||||
val layoutManager = (b.list.layoutManager as? LinearLayoutManager)
|
val layoutManager = (b.list.layoutManager as? LinearLayoutManager)
|
||||||
val searchItem = adapter.items.firstOrNull { it is MessagesSearch } as? MessagesSearch
|
val searchField = adapter.getSearchField()
|
||||||
|
|
||||||
onPageDestroy?.invoke(position, Bundle(
|
onPageDestroy?.invoke(position, Bundle(
|
||||||
"recyclerViewState" to layoutManager?.onSaveInstanceState(),
|
"recyclerViewState" to layoutManager?.onSaveInstanceState(),
|
||||||
"searchText" to searchItem?.searchText?.toString()
|
"searchText" to searchField?.searchText?.toString()
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,9 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Kuba Szczodrzyński 2020-4-5.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package pl.szczodrzynski.edziennik.ui.modules.messages.models
|
|
||||||
|
|
||||||
class MessagesSearch {
|
|
||||||
var searchText: CharSequence = ""
|
|
||||||
}
|
|
@ -2,7 +2,7 @@
|
|||||||
* Copyright (c) Kuba Szczodrzyński 2019-11-12.
|
* Copyright (c) Kuba Szczodrzyński 2019-11-12.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pl.szczodrzynski.edziennik.ui.modules.messages
|
package pl.szczodrzynski.edziennik.ui.modules.messages.single
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.Html
|
import android.text.Html
|
||||||
@ -25,11 +25,11 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
|||||||
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
|
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
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.LoginStore.Companion.LOGIN_TYPE_IDZIENNIK
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_DELETED
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||||
import pl.szczodrzynski.edziennik.databinding.MessageFragmentBinding
|
import pl.szczodrzynski.edziennik.databinding.MessageFragmentBinding
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.MessagesConfigDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.MessagesConfigDialog
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesUtils
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.messages.list.MessagesFragment
|
||||||
import pl.szczodrzynski.edziennik.utils.Anim
|
import pl.szczodrzynski.edziennik.utils.Anim
|
||||||
import pl.szczodrzynski.edziennik.utils.BetterLink
|
import pl.szczodrzynski.edziennik.utils.BetterLink
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
@ -188,7 +188,7 @@ class MessageFragment : Fragment(), CoroutineScope {
|
|||||||
|
|
||||||
if (app.profile.loginStoreType == LoginStore.LOGIN_TYPE_VULCAN) {
|
if (app.profile.loginStoreType == LoginStore.LOGIN_TYPE_VULCAN) {
|
||||||
// vulcan: change message status or download attachments
|
// vulcan: change message status or download attachments
|
||||||
if (message.type == TYPE_RECEIVED && !message.seen || message.attachmentIds == null) {
|
if ((message.isReceived || message.isDeleted) && !message.seen || message.attachmentIds == null) {
|
||||||
EdziennikTask.messageGet(App.profileId, message).enqueue(activity)
|
EdziennikTask.messageGet(App.profileId, message).enqueue(activity)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -214,9 +214,9 @@ class MessageFragment : Fragment(), CoroutineScope {
|
|||||||
|
|
||||||
manager.setStarIcon(b.messageStar, message)
|
manager.setStarIcon(b.messageStar, message)
|
||||||
|
|
||||||
b.replyButton.isVisible = message.type == TYPE_RECEIVED || message.type == TYPE_DELETED
|
b.replyButton.isVisible = message.isReceived || message.isDeleted
|
||||||
b.deleteButton.isVisible = message.type == TYPE_RECEIVED
|
b.deleteButton.isVisible = message.isReceived
|
||||||
if (message.type == TYPE_RECEIVED || message.type == TYPE_DELETED) {
|
if (message.isReceived || message.isDeleted) {
|
||||||
activity.navView.apply {
|
activity.navView.apply {
|
||||||
bottomBar.apply {
|
bottomBar.apply {
|
||||||
fabEnable = true
|
fabEnable = true
|
@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Kuba Szczodrzyński 2021-4-14.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package pl.szczodrzynski.edziennik.ui.modules.messages.utils
|
|
||||||
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
|
||||||
|
|
||||||
class MessagesComparator : Comparator<Any> {
|
|
||||||
|
|
||||||
override fun compare(o1: Any?, o2: Any?): Int {
|
|
||||||
if (o1 !is MessageFull || o2 !is MessageFull)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
return when {
|
|
||||||
// descending sorting (1. true, 2. false)
|
|
||||||
o1.isStarred && !o2.isStarred -> -1
|
|
||||||
!o1.isStarred && o2.isStarred -> 1
|
|
||||||
// ascending sorting
|
|
||||||
o1.filterWeight > o2.filterWeight -> 1
|
|
||||||
o1.filterWeight < o2.filterWeight -> -1
|
|
||||||
// descending sorting
|
|
||||||
o1.addedDate > o2.addedDate -> -1
|
|
||||||
o1.addedDate < o2.addedDate -> 1
|
|
||||||
else -> 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-4-5.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.modules.search
|
||||||
|
|
||||||
|
import android.widget.Filter
|
||||||
|
|
||||||
|
class SearchField(
|
||||||
|
var searchText: CharSequence = "",
|
||||||
|
) : Searchable<SearchField> {
|
||||||
|
|
||||||
|
override val searchKeywords = emptyList<List<String>>()
|
||||||
|
override var searchPriority = 0
|
||||||
|
override var searchHighlightText: String? = null
|
||||||
|
override fun compareTo(other: Searchable<*>) = 0
|
||||||
|
|
||||||
|
fun applyTo(adapter: SearchableAdapter<*>, listener: Filter.FilterListener? = null) {
|
||||||
|
adapter.filter.filter(searchText, listener)
|
||||||
|
}
|
||||||
|
}
|
@ -2,24 +2,20 @@
|
|||||||
* Copyright (c) Kuba Szczodrzyński 2021-4-14.
|
* Copyright (c) Kuba Szczodrzyński 2021-4-14.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pl.szczodrzynski.edziennik.ui.modules.messages.utils
|
package pl.szczodrzynski.edziennik.ui.modules.search
|
||||||
|
|
||||||
import android.widget.Filter
|
import android.widget.Filter
|
||||||
import pl.szczodrzynski.edziennik.cleanDiacritics
|
import pl.szczodrzynski.edziennik.cleanDiacritics
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesAdapter
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
class MessagesFilter(
|
class SearchFilter<T : Searchable<T>>(
|
||||||
private val adapter: MessagesAdapter
|
private val adapter: SearchableAdapter<T>,
|
||||||
) : Filter() {
|
) : Filter() {
|
||||||
companion object {
|
companion object {
|
||||||
private const val NO_MATCH = 1000
|
private const val NO_MATCH = 1000
|
||||||
}
|
}
|
||||||
|
|
||||||
private val comparator = MessagesComparator()
|
|
||||||
private var prevCount = -1
|
private var prevCount = -1
|
||||||
|
|
||||||
private val allItems
|
private val allItems
|
||||||
@ -54,75 +50,58 @@ class MessagesFilter(
|
|||||||
|
|
||||||
if (prefix.isNullOrBlank()) {
|
if (prefix.isNullOrBlank()) {
|
||||||
allItems.forEach {
|
allItems.forEach {
|
||||||
if (it is MessageFull)
|
it.searchPriority = NO_MATCH
|
||||||
it.searchHighlightText = null
|
it.searchHighlightText = null
|
||||||
}
|
}
|
||||||
results.values = allItems.toList()
|
results.values = allItems.toList()
|
||||||
results.count = allItems.size
|
results.count = allItems.size
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
val items = mutableListOf<Any>()
|
val newItems = allItems.mapNotNull { item ->
|
||||||
|
if (item is SearchField) {
|
||||||
allItems.forEach {
|
return@mapNotNull item
|
||||||
if (it !is MessageFull) {
|
|
||||||
items.add(it)
|
|
||||||
return@forEach
|
|
||||||
}
|
}
|
||||||
it.filterWeight = NO_MATCH
|
item.searchPriority = NO_MATCH
|
||||||
it.searchHighlightText = null
|
item.searchHighlightText = null
|
||||||
|
|
||||||
var weight: Int
|
// get all keyword sets from the entity
|
||||||
// weights 11..13 and 110
|
val searchKeywords = item.searchKeywords
|
||||||
if (it.type == Message.TYPE_SENT) {
|
// a temporary variable for the loops below
|
||||||
it.recipients?.forEach { recipient ->
|
var matchWeight: Int
|
||||||
weight = getMatchWeight(recipient.fullName, prefix)
|
|
||||||
if (weight != NO_MATCH) {
|
searchKeywords.forEachIndexed { priority, keywords ->
|
||||||
if (weight == 3)
|
keywords ?: return@forEachIndexed
|
||||||
weight = 100
|
keywords.forEach { keyword ->
|
||||||
it.filterWeight = min(it.filterWeight, 10 + weight)
|
matchWeight = getMatchWeight(keyword, prefix)
|
||||||
|
if (matchWeight != NO_MATCH) {
|
||||||
|
// a match not at the word start boundary should be least prioritized
|
||||||
|
if (matchWeight == 3)
|
||||||
|
matchWeight = 100
|
||||||
|
item.searchPriority = min(item.searchPriority, priority * 10 + matchWeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
weight = getMatchWeight(it.senderName, prefix)
|
|
||||||
if (weight != NO_MATCH) {
|
|
||||||
if (weight == 3)
|
|
||||||
weight = 100
|
|
||||||
it.filterWeight = min(it.filterWeight, 10 + weight)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// weights 21..23 and 120
|
if (item.searchPriority != NO_MATCH) {
|
||||||
weight = getMatchWeight(it.subject, prefix)
|
// the adapter is reversed, the search priority also should be
|
||||||
if (weight != NO_MATCH) {
|
if (adapter.isReversed)
|
||||||
if (weight == 3)
|
item.searchPriority *= -1
|
||||||
weight = 100
|
item.searchHighlightText = prefix.toString()
|
||||||
it.filterWeight = min(it.filterWeight, 20 + weight)
|
return@mapNotNull item
|
||||||
}
|
|
||||||
|
|
||||||
// weights 31..33 and 130
|
|
||||||
weight = getMatchWeight(it.body, prefix)
|
|
||||||
if (weight != NO_MATCH) {
|
|
||||||
if (weight == 3)
|
|
||||||
weight = 100
|
|
||||||
it.filterWeight = min(it.filterWeight, 30 + weight)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (it.filterWeight != NO_MATCH) {
|
|
||||||
it.searchHighlightText = prefix
|
|
||||||
items.add(it)
|
|
||||||
}
|
}
|
||||||
|
return@mapNotNull null
|
||||||
}
|
}
|
||||||
|
|
||||||
Collections.sort(items, comparator)
|
results.values = newItems.sorted()
|
||||||
results.values = items
|
results.count = newItems.size
|
||||||
results.count = items.size
|
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun publishResults(constraint: CharSequence?, results: FilterResults) {
|
override fun publishResults(constraint: CharSequence?, results: FilterResults) {
|
||||||
results.values?.let {
|
results.values?.let {
|
||||||
adapter.items = it as MutableList<Any>
|
@Suppress("UNCHECKED_CAST") // yes I know it's checked.
|
||||||
|
adapter.setFilteredItems(it as List<T>)
|
||||||
}
|
}
|
||||||
// do not re-bind the search box
|
// do not re-bind the search box
|
||||||
val count = results.count - 1
|
val count = results.count - 1
|
@ -2,18 +2,17 @@
|
|||||||
* Copyright (c) Kuba Szczodrzyński 2021-4-14.
|
* Copyright (c) Kuba Szczodrzyński 2021-4-14.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pl.szczodrzynski.edziennik.ui.modules.messages.utils
|
package pl.szczodrzynski.edziennik.ui.modules.search
|
||||||
|
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
import pl.szczodrzynski.edziennik.R
|
import pl.szczodrzynski.edziennik.R
|
||||||
import pl.szczodrzynski.edziennik.databinding.MessagesListItemSearchBinding
|
import pl.szczodrzynski.edziennik.databinding.SearchItemBinding
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.models.MessagesSearch
|
|
||||||
|
|
||||||
class SearchTextWatcher(
|
class SearchTextWatcher(
|
||||||
private val b: MessagesListItemSearchBinding,
|
private val b: SearchItemBinding,
|
||||||
private val filter: MessagesFilter,
|
private val filter: SearchFilter<*>,
|
||||||
private val item: MessagesSearch
|
private val item: SearchField,
|
||||||
) : TextWatcher {
|
) : TextWatcher {
|
||||||
|
|
||||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
|
@ -2,40 +2,28 @@
|
|||||||
* Copyright (c) Kuba Szczodrzyński 2020-4-5.
|
* Copyright (c) Kuba Szczodrzyński 2020-4-5.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pl.szczodrzynski.edziennik.ui.modules.messages.viewholder
|
package pl.szczodrzynski.edziennik.ui.modules.search
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import pl.szczodrzynski.edziennik.App
|
|
||||||
import pl.szczodrzynski.edziennik.R
|
import pl.szczodrzynski.edziennik.R
|
||||||
import pl.szczodrzynski.edziennik.databinding.MessagesListItemSearchBinding
|
import pl.szczodrzynski.edziennik.databinding.SearchItemBinding
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
|
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesAdapter
|
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.models.MessagesSearch
|
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.messages.utils.SearchTextWatcher
|
|
||||||
|
|
||||||
class SearchViewHolder(
|
class SearchViewHolder(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
parent: ViewGroup,
|
parent: ViewGroup,
|
||||||
val b: MessagesListItemSearchBinding = MessagesListItemSearchBinding.inflate(
|
val b: SearchItemBinding = SearchItemBinding.inflate(
|
||||||
inflater,
|
inflater,
|
||||||
parent,
|
parent,
|
||||||
false
|
false
|
||||||
)
|
),
|
||||||
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<MessagesSearch, MessagesAdapter> {
|
) : RecyclerView.ViewHolder(b.root) {
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "SearchViewHolder"
|
private const val TAG = "SearchViewHolder"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBind(
|
internal fun bind(item: SearchField, adapter: SearchableAdapter<*>) {
|
||||||
activity: AppCompatActivity,
|
|
||||||
app: App,
|
|
||||||
item: MessagesSearch,
|
|
||||||
position: Int,
|
|
||||||
adapter: MessagesAdapter
|
|
||||||
) {
|
|
||||||
val watcher = SearchTextWatcher(b, adapter.filter, item)
|
val watcher = SearchTextWatcher(b, adapter.filter, item)
|
||||||
b.searchEdit.removeTextChangedListener(watcher)
|
b.searchEdit.removeTextChangedListener(watcher)
|
||||||
|
|
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2021-10-10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.modules.search
|
||||||
|
|
||||||
|
interface Searchable<in T> : Comparable<Searchable<*>> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A prioritized list of keywords sets. First items are of the highest priority.
|
||||||
|
* Items within a keyword set have the same priority.
|
||||||
|
*/
|
||||||
|
val searchKeywords: List<List<String?>?>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A priority assigned by [SearchFilter]. Lower numbers mean a higher priority.
|
||||||
|
*/
|
||||||
|
var searchPriority: Int
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The text to be highlighted when filtering.
|
||||||
|
*/
|
||||||
|
var searchHighlightText: String?
|
||||||
|
}
|
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2021-10-10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.modules.search
|
||||||
|
|
||||||
|
import android.text.SpannableStringBuilder
|
||||||
|
import android.text.style.BackgroundColorSpan
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Filterable
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import pl.szczodrzynski.edziennik.asSpannable
|
||||||
|
import pl.szczodrzynski.edziennik.utils.span.BoldSpan
|
||||||
|
|
||||||
|
abstract class SearchableAdapter<T : Searchable<T>>(
|
||||||
|
val isReversed: Boolean = false,
|
||||||
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), Filterable {
|
||||||
|
companion object {
|
||||||
|
const val ITEM_TYPE_SEARCH = 2137
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mutable list managed by [setAllItems].
|
||||||
|
* Items are never displayed straight from this list.
|
||||||
|
* Items in this list are always sorted according to their
|
||||||
|
* natural order, with the [SearchField] preceding any other.
|
||||||
|
*/
|
||||||
|
val allItems = mutableListOf<T>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mutable var changed by the [SearchFilter].
|
||||||
|
* This list is the only direct source of displayed items.
|
||||||
|
* Items in this list may be in reverse order ([isReversed]), with the [SearchField]
|
||||||
|
* still as the first item.
|
||||||
|
*/
|
||||||
|
var items = listOf<T>()
|
||||||
|
private set
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set [items] as the currently displayed item list. The [items] are first
|
||||||
|
* sorted appropriately to the [isReversed] property.
|
||||||
|
*/
|
||||||
|
internal fun setFilteredItems(items: List<T>) {
|
||||||
|
this.items = if (isReversed)
|
||||||
|
items.sortedDescending() // the sort is stable - SearchField should stay at the top
|
||||||
|
else
|
||||||
|
items.sorted()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put [items] to the sorted, unfiltered data source.
|
||||||
|
*
|
||||||
|
* @param searchText the text to fill the [SearchField] with, by default
|
||||||
|
* @param addSearchField whether searching should be enabled and visible
|
||||||
|
*/
|
||||||
|
fun setAllItems(items: List<T>, searchText: String? = null, addSearchField: Boolean = false) {
|
||||||
|
if (allItems.isEmpty()) {
|
||||||
|
// items empty - add the search field
|
||||||
|
if (addSearchField) {
|
||||||
|
@Suppress("UNCHECKED_CAST") // what ???
|
||||||
|
allItems += SearchField(searchText ?: "") as T
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// items not empty - remove all except the search field
|
||||||
|
allItems.removeAll { it !is SearchField }
|
||||||
|
}
|
||||||
|
// add all new items
|
||||||
|
allItems.addAll(items.sorted())
|
||||||
|
// show all items if searching is disabled
|
||||||
|
if (!addSearchField) {
|
||||||
|
setFilteredItems(allItems)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the search field in this adapter's list, or null if not found.
|
||||||
|
*/
|
||||||
|
fun getSearchField(): SearchField? {
|
||||||
|
return allItems.filterIsInstance<SearchField>().firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun highlightSearchText(item: T, text: CharSequence, color: Int): CharSequence {
|
||||||
|
if (item.searchHighlightText == null)
|
||||||
|
return SpannableStringBuilder(text)
|
||||||
|
return text.asSpannable(
|
||||||
|
BoldSpan(),
|
||||||
|
BackgroundColorSpan(color),
|
||||||
|
substring = item.searchHighlightText,
|
||||||
|
ignoreCase = true,
|
||||||
|
ignoreDiacritics = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun onCreateViewHolder(
|
||||||
|
parent: ViewGroup,
|
||||||
|
viewType: Int,
|
||||||
|
): RecyclerView.ViewHolder {
|
||||||
|
val inflater = LayoutInflater.from(parent.context)
|
||||||
|
return when (viewType) {
|
||||||
|
ITEM_TYPE_SEARCH -> SearchViewHolder(inflater, parent)
|
||||||
|
else -> onCreateViewHolder(inflater, parent, viewType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun getItemViewType(position: Int): Int {
|
||||||
|
return when (val item = items[position]) {
|
||||||
|
is SearchField -> ITEM_TYPE_SEARCH
|
||||||
|
else -> getItemViewType(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
|
val item = items[position]
|
||||||
|
if (holder is SearchViewHolder && item is SearchField) {
|
||||||
|
holder.bind(item, this)
|
||||||
|
} else {
|
||||||
|
onBindViewHolder(holder, position, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun getItemViewType(item: T): Int
|
||||||
|
abstract fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int, item: T)
|
||||||
|
abstract fun onCreateViewHolder(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
parent: ViewGroup,
|
||||||
|
viewType: Int,
|
||||||
|
): RecyclerView.ViewHolder
|
||||||
|
|
||||||
|
private val filter = SearchFilter(this)
|
||||||
|
override fun getItemCount() = items.size
|
||||||
|
override fun getFilter() = filter
|
||||||
|
}
|
@ -26,8 +26,8 @@ import pl.szczodrzynski.edziennik.R
|
|||||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||||
import pl.szczodrzynski.edziennik.databinding.FragmentTimetableV2Binding
|
import pl.szczodrzynski.edziennik.databinding.FragmentTimetableV2Binding
|
||||||
import pl.szczodrzynski.edziennik.getSchoolYearConstrains
|
import pl.szczodrzynski.edziennik.getSchoolYearConstrains
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
|
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.timetable.GenerateBlockTimetableDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.timetable.GenerateBlockTimetableDialog
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.event.EventManualDialog
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
||||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.utils.managers
|
package pl.szczodrzynski.edziennik.utils.managers
|
||||||
|
|
||||||
|
import android.widget.TextView
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import com.mikepenz.iconics.IconicsDrawable
|
import com.mikepenz.iconics.IconicsDrawable
|
||||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||||
@ -39,7 +40,7 @@ class EventManager(val app: App) : CoroutineScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setEventTopic(
|
fun setEventTopic(
|
||||||
title: IconicsTextView,
|
title: TextView,
|
||||||
event: EventFull,
|
event: EventFull,
|
||||||
showType: Boolean = true,
|
showType: Boolean = true,
|
||||||
doneIconColor: Int? = null
|
doneIconColor: Int? = null
|
||||||
|
@ -95,12 +95,12 @@ class MessageManager(private val app: App) {
|
|||||||
recipient.fullName = teachers.firstOrNull { it.id == recipient.id }?.fullName ?: ""
|
recipient.fullName = teachers.firstOrNull { it.id == recipient.id }?.fullName ?: ""
|
||||||
|
|
||||||
// unset the readByEveryone flag
|
// unset the readByEveryone flag
|
||||||
if (recipient.readDate < 1 && message.type == Message.TYPE_SENT)
|
if (recipient.readDate < 1 && message.isSent)
|
||||||
message.readByEveryone = false
|
message.readByEveryone = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// store the account name as sender for sent messages
|
// store the account name as sender for sent messages
|
||||||
if (message.type == Message.TYPE_SENT && message.senderName == null) {
|
if (message.isSent && message.senderName == null) {
|
||||||
message.senderName = app.profile.accountName ?: app.profile.studentNameLong
|
message.senderName = app.profile.accountName ?: app.profile.studentNameLong
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +193,7 @@ class MessageManager(private val app: App) {
|
|||||||
else null
|
else null
|
||||||
|
|
||||||
when {
|
when {
|
||||||
message != null && message.type == Message.TYPE_DRAFT -> {
|
message != null && message.isDraft -> {
|
||||||
fillWithDraftMessage(config, message)
|
fillWithDraftMessage(config, message)
|
||||||
}
|
}
|
||||||
message != null -> {
|
message != null -> {
|
||||||
|
@ -2,12 +2,14 @@
|
|||||||
<!--
|
<!--
|
||||||
~ Copyright (c) Kuba Szczodrzyński 2019-12-15.
|
~ Copyright (c) Kuba Szczodrzyński 2019-12-15.
|
||||||
-->
|
-->
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
<layout xmlns:tools="http://schemas.android.com/tools"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<data>
|
<data>
|
||||||
<import type="android.view.View"/>
|
|
||||||
|
<import type="android.view.View" />
|
||||||
|
|
||||||
<variable
|
<variable
|
||||||
name="simpleMode"
|
name="simpleMode"
|
||||||
type="Boolean" />
|
type="Boolean" />
|
||||||
@ -16,9 +18,9 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="8dp"
|
android:background="?selectableItemBackground"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:background="?selectableItemBackground">
|
android:padding="8dp">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -33,14 +35,24 @@
|
|||||||
android:layout_marginRight="8dp"
|
android:layout_marginRight="8dp"
|
||||||
android:background="@drawable/unread_red_circle" />
|
android:background="@drawable/unread_red_circle" />
|
||||||
|
|
||||||
|
<com.mikepenz.iconics.view.IconicsImageView
|
||||||
|
android:id="@+id/attachmentIcon"
|
||||||
|
android:layout_width="16dp"
|
||||||
|
android:layout_height="16dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
app:iiv_color="?android:textColorSecondary"
|
||||||
|
app:iiv_icon="cmd-attachment"
|
||||||
|
tools:background="@tools:sample/avatars[4]" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/details"
|
android:id="@+id/details"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
|
android:maxLines="2"
|
||||||
android:textAppearance="@style/NavView.TextView.Helper"
|
android:textAppearance="@style/NavView.TextView.Helper"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:maxLines="2"
|
|
||||||
tools:text="sprawdzian • 9:05 • historia i społeczeństwo" />
|
tools:text="sprawdzian • 9:05 • historia i społeczeństwo" />
|
||||||
|
|
||||||
<View
|
<View
|
||||||
@ -48,10 +60,9 @@
|
|||||||
android:layout_width="12dp"
|
android:layout_width="12dp"
|
||||||
android:layout_height="12dp"
|
android:layout_height="12dp"
|
||||||
android:layout_marginHorizontal="4dp"
|
android:layout_marginHorizontal="4dp"
|
||||||
android:visibility="gone"
|
|
||||||
android:background="@drawable/unread_red_circle"
|
android:background="@drawable/unread_red_circle"
|
||||||
tools:visibility="visible"/>
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@ -60,7 +71,7 @@
|
|||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<com.mikepenz.iconics.view.IconicsTextView
|
<TextView
|
||||||
android:id="@+id/topic"
|
android:id="@+id/topic"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@ -69,7 +80,6 @@
|
|||||||
android:maxLines="3"
|
android:maxLines="3"
|
||||||
android:textAppearance="@style/NavView.TextView.Medium"
|
android:textAppearance="@style/NavView.TextView.Medium"
|
||||||
tools:text="Rozdział II: Panowanie Piastów i Jagiellonów.Przeniesiony z 11 grudnia. Nie wiem co się dzieje w tym roku nie będzie już religii w szkołach podstawowych w Polsce i Europie zachodniej Afryki" />
|
tools:text="Rozdział II: Panowanie Piastów i Jagiellonów.Przeniesiony z 11 grudnia. Nie wiem co się dzieje w tym roku nie będzie już religii w szkołach podstawowych w Polsce i Europie zachodniej Afryki" />
|
||||||
|
|
||||||
<!-- cmd_pencil_outline -->
|
<!-- cmd_pencil_outline -->
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/editButton"
|
android:id="@+id/editButton"
|
||||||
@ -85,13 +95,13 @@
|
|||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<com.mikepenz.iconics.view.IconicsTextView
|
<TextView
|
||||||
android:id="@+id/addedBy"
|
android:id="@+id/addedBy"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textAppearance="@style/NavView.TextView.Helper"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:ellipsize="middle"
|
android:ellipsize="middle"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="@style/NavView.TextView.Helper"
|
||||||
android:visibility="@{simpleMode ? View.GONE : View.VISIBLE}"
|
android:visibility="@{simpleMode ? View.GONE : View.VISIBLE}"
|
||||||
tools:text="Udostępniono 10 grudnia przez Ktoś Z Twojej Klasy • 2B3T" />
|
tools:text="Udostępniono 10 grudnia przez Ktoś Z Twojej Klasy • 2B3T" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -114,13 +114,10 @@
|
|||||||
|
|
||||||
<com.mikepenz.iconics.view.IconicsImageView
|
<com.mikepenz.iconics.view.IconicsImageView
|
||||||
android:id="@+id/messageAttachmentImage"
|
android:id="@+id/messageAttachmentImage"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="16dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="16dp"
|
||||||
android:layout_marginEnd="4dp"
|
android:layout_marginEnd="4dp"
|
||||||
android:layout_marginRight="4dp"
|
android:layout_marginRight="4dp"
|
||||||
android:adjustViewBounds="true"
|
|
||||||
android:paddingVertical="2dp"
|
|
||||||
android:scaleType="fitCenter"
|
|
||||||
app:iiv_color="?android:textColorSecondary"
|
app:iiv_color="?android:textColorSecondary"
|
||||||
app:iiv_icon="cmd-attachment"
|
app:iiv_icon="cmd-attachment"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/messageDate"
|
app:layout_constraintBottom_toBottomOf="@+id/messageDate"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user