mirror of
https://github.com/wulkanowy/wulkanowy.git
synced 2025-01-31 13:08:21 +01:00
Add message attachments (#734)
This commit is contained in:
parent
da357775ff
commit
502a98b70a
@ -128,7 +128,7 @@ configurations.all {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "io.github.wulkanowy:sdk:4ea879b"
|
implementation "io.github.wulkanowy:sdk:44725a9"
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
implementation "androidx.core:core-ktx:1.2.0"
|
implementation "androidx.core:core-ktx:1.2.0"
|
||||||
|
1732
app/schemas/io.github.wulkanowy.data.db.AppDatabase/24.json
Normal file
1732
app/schemas/io.github.wulkanowy.data.db.AppDatabase/24.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -97,6 +97,10 @@ internal class RepositoryModule {
|
|||||||
@Provides
|
@Provides
|
||||||
fun provideMessagesDao(database: AppDatabase) = database.messagesDao
|
fun provideMessagesDao(database: AppDatabase) = database.messagesDao
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
fun provideMessageAttachmentsDao(database: AppDatabase) = database.messageAttachmentDao
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideExamDao(database: AppDatabase) = database.examsDao
|
fun provideExamDao(database: AppDatabase) = database.examsDao
|
||||||
|
@ -17,6 +17,7 @@ import io.github.wulkanowy.data.db.dao.GradeStatisticsDao
|
|||||||
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
|
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
|
||||||
import io.github.wulkanowy.data.db.dao.HomeworkDao
|
import io.github.wulkanowy.data.db.dao.HomeworkDao
|
||||||
import io.github.wulkanowy.data.db.dao.LuckyNumberDao
|
import io.github.wulkanowy.data.db.dao.LuckyNumberDao
|
||||||
|
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
|
||||||
import io.github.wulkanowy.data.db.dao.MessagesDao
|
import io.github.wulkanowy.data.db.dao.MessagesDao
|
||||||
import io.github.wulkanowy.data.db.dao.MobileDeviceDao
|
import io.github.wulkanowy.data.db.dao.MobileDeviceDao
|
||||||
import io.github.wulkanowy.data.db.dao.NoteDao
|
import io.github.wulkanowy.data.db.dao.NoteDao
|
||||||
@ -39,6 +40,7 @@ import io.github.wulkanowy.data.db.entities.GradeSummary
|
|||||||
import io.github.wulkanowy.data.db.entities.Homework
|
import io.github.wulkanowy.data.db.entities.Homework
|
||||||
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
||||||
import io.github.wulkanowy.data.db.entities.Message
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
|
import io.github.wulkanowy.data.db.entities.MessageAttachment
|
||||||
import io.github.wulkanowy.data.db.entities.MobileDevice
|
import io.github.wulkanowy.data.db.entities.MobileDevice
|
||||||
import io.github.wulkanowy.data.db.entities.Note
|
import io.github.wulkanowy.data.db.entities.Note
|
||||||
import io.github.wulkanowy.data.db.entities.Recipient
|
import io.github.wulkanowy.data.db.entities.Recipient
|
||||||
@ -64,6 +66,7 @@ import io.github.wulkanowy.data.db.migrations.Migration20
|
|||||||
import io.github.wulkanowy.data.db.migrations.Migration21
|
import io.github.wulkanowy.data.db.migrations.Migration21
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration22
|
import io.github.wulkanowy.data.db.migrations.Migration22
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration23
|
import io.github.wulkanowy.data.db.migrations.Migration23
|
||||||
|
import io.github.wulkanowy.data.db.migrations.Migration24
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration3
|
import io.github.wulkanowy.data.db.migrations.Migration3
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration4
|
import io.github.wulkanowy.data.db.migrations.Migration4
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration5
|
import io.github.wulkanowy.data.db.migrations.Migration5
|
||||||
@ -87,6 +90,7 @@ import javax.inject.Singleton
|
|||||||
GradeStatistics::class,
|
GradeStatistics::class,
|
||||||
GradePointsStatistics::class,
|
GradePointsStatistics::class,
|
||||||
Message::class,
|
Message::class,
|
||||||
|
MessageAttachment::class,
|
||||||
Note::class,
|
Note::class,
|
||||||
Homework::class,
|
Homework::class,
|
||||||
Subject::class,
|
Subject::class,
|
||||||
@ -105,7 +109,7 @@ import javax.inject.Singleton
|
|||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val VERSION_SCHEMA = 23
|
const val VERSION_SCHEMA = 24
|
||||||
|
|
||||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
|
fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
|
||||||
return arrayOf(
|
return arrayOf(
|
||||||
@ -130,7 +134,8 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
Migration20(),
|
Migration20(),
|
||||||
Migration21(),
|
Migration21(),
|
||||||
Migration22(),
|
Migration22(),
|
||||||
Migration23()
|
Migration23(),
|
||||||
|
Migration24()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,6 +171,8 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
|
|
||||||
abstract val messagesDao: MessagesDao
|
abstract val messagesDao: MessagesDao
|
||||||
|
|
||||||
|
abstract val messageAttachmentDao: MessageAttachmentDao
|
||||||
|
|
||||||
abstract val noteDao: NoteDao
|
abstract val noteDao: NoteDao
|
||||||
|
|
||||||
abstract val homeworkDao: HomeworkDao
|
abstract val homeworkDao: HomeworkDao
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
package io.github.wulkanowy.data.db.dao
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import io.github.wulkanowy.data.db.entities.MessageAttachment
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface MessageAttachmentDao : BaseDao<MessageAttachment> {
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun insertAttachments(items: List<MessageAttachment>): List<Long>
|
||||||
|
}
|
@ -2,19 +2,22 @@ package io.github.wulkanowy.data.db.dao
|
|||||||
|
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
|
import androidx.room.Transaction
|
||||||
import io.github.wulkanowy.data.db.entities.Message
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
|
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
|
||||||
import io.reactivex.Maybe
|
import io.reactivex.Maybe
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface MessagesDao : BaseDao<Message> {
|
interface MessagesDao : BaseDao<Message> {
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND message_id = :messageId")
|
||||||
|
fun loadMessageWithAttachment(studentId: Int, messageId: Int): Single<MessageWithAttachment>
|
||||||
|
|
||||||
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder AND removed = 0 ORDER BY date DESC")
|
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder AND removed = 0 ORDER BY date DESC")
|
||||||
fun loadAll(studentId: Int, folder: Int): Maybe<List<Message>>
|
fun loadAll(studentId: Int, folder: Int): Maybe<List<Message>>
|
||||||
|
|
||||||
@Query("SELECT * FROM Messages WHERE id = :id")
|
|
||||||
fun load(id: Long): Single<Message>
|
|
||||||
|
|
||||||
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND removed = 1 ORDER BY date DESC")
|
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND removed = 1 ORDER BY date DESC")
|
||||||
fun loadDeleted(studentId: Int): Maybe<List<Message>>
|
fun loadDeleted(studentId: Int): Maybe<List<Message>>
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,10 @@ data class Message(
|
|||||||
@ColumnInfo(name = "read_by")
|
@ColumnInfo(name = "read_by")
|
||||||
val readBy: Int,
|
val readBy: Int,
|
||||||
|
|
||||||
val removed: Boolean
|
val removed: Boolean,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "has_attachments")
|
||||||
|
val hasAttachments: Boolean
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
package io.github.wulkanowy.data.db.entities
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
@Entity(tableName = "MessageAttachments")
|
||||||
|
data class MessageAttachment(
|
||||||
|
|
||||||
|
@PrimaryKey
|
||||||
|
@ColumnInfo(name = "real_id")
|
||||||
|
val realId: Int,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "message_id")
|
||||||
|
val messageId: Int,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "one_drive_id")
|
||||||
|
val oneDriveId: String,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "url")
|
||||||
|
val url: String,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "filename")
|
||||||
|
val filename: String
|
||||||
|
) : Serializable
|
@ -0,0 +1,12 @@
|
|||||||
|
package io.github.wulkanowy.data.db.entities
|
||||||
|
|
||||||
|
import androidx.room.Embedded
|
||||||
|
import androidx.room.Relation
|
||||||
|
|
||||||
|
data class MessageWithAttachment(
|
||||||
|
@Embedded
|
||||||
|
val message: Message,
|
||||||
|
|
||||||
|
@Relation(parentColumn = "message_id", entityColumn = "message_id")
|
||||||
|
val attachments: List<MessageAttachment>
|
||||||
|
)
|
@ -0,0 +1,21 @@
|
|||||||
|
package io.github.wulkanowy.data.db.migrations
|
||||||
|
|
||||||
|
import androidx.room.migration.Migration
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
|
||||||
|
class Migration24 : Migration(23, 24) {
|
||||||
|
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("ALTER TABLE Messages ADD COLUMN has_attachments INTEGER NOT NULL DEFAULT 0")
|
||||||
|
database.execSQL("""
|
||||||
|
CREATE TABLE IF NOT EXISTS MessageAttachments (
|
||||||
|
real_id INTEGER NOT NULL,
|
||||||
|
message_id INTEGER NOT NULL,
|
||||||
|
one_drive_id TEXT NOT NULL,
|
||||||
|
url TEXT NOT NULL,
|
||||||
|
filename TEXT NOT NULL,
|
||||||
|
PRIMARY KEY(real_id)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,10 @@
|
|||||||
package io.github.wulkanowy.data.repositories.message
|
package io.github.wulkanowy.data.repositories.message
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
|
||||||
import io.github.wulkanowy.data.db.dao.MessagesDao
|
import io.github.wulkanowy.data.db.dao.MessagesDao
|
||||||
import io.github.wulkanowy.data.db.entities.Message
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
|
import io.github.wulkanowy.data.db.entities.MessageAttachment
|
||||||
|
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED
|
import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED
|
||||||
import io.reactivex.Maybe
|
import io.reactivex.Maybe
|
||||||
@ -10,7 +13,10 @@ import javax.inject.Inject
|
|||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class MessageLocal @Inject constructor(private val messagesDb: MessagesDao) {
|
class MessageLocal @Inject constructor(
|
||||||
|
private val messagesDb: MessagesDao,
|
||||||
|
private val messageAttachmentDao: MessageAttachmentDao
|
||||||
|
) {
|
||||||
|
|
||||||
fun saveMessages(messages: List<Message>) {
|
fun saveMessages(messages: List<Message>) {
|
||||||
messagesDb.insertAll(messages)
|
messagesDb.insertAll(messages)
|
||||||
@ -24,8 +30,12 @@ class MessageLocal @Inject constructor(private val messagesDb: MessagesDao) {
|
|||||||
messagesDb.deleteAll(messages)
|
messagesDb.deleteAll(messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMessage(id: Long): Single<Message> {
|
fun getMessageWithAttachment(student: Student, message: Message): Single<MessageWithAttachment> {
|
||||||
return messagesDb.load(id)
|
return messagesDb.loadMessageWithAttachment(student.id.toInt(), message.messageId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveMessageAttachments(attachments: List<MessageAttachment>) {
|
||||||
|
messageAttachmentDao.insertAttachments(attachments)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMessages(student: Student, folder: MessageFolder): Maybe<List<Message>> {
|
fun getMessages(student: Student, folder: MessageFolder): Maybe<List<Message>> {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package io.github.wulkanowy.data.repositories.message
|
package io.github.wulkanowy.data.repositories.message
|
||||||
|
|
||||||
import io.github.wulkanowy.data.db.entities.Message
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
|
import io.github.wulkanowy.data.db.entities.MessageAttachment
|
||||||
import io.github.wulkanowy.data.db.entities.Recipient
|
import io.github.wulkanowy.data.db.entities.Recipient
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
@ -33,14 +34,25 @@ class MessageRemote @Inject constructor(private val sdk: Sdk) {
|
|||||||
unread = it.unread ?: false,
|
unread = it.unread ?: false,
|
||||||
unreadBy = it.unreadBy ?: 0,
|
unreadBy = it.unreadBy ?: 0,
|
||||||
readBy = it.readBy ?: 0,
|
readBy = it.readBy ?: 0,
|
||||||
removed = it.removed
|
removed = it.removed,
|
||||||
|
hasAttachments = it.hasAttachments
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMessagesContent(message: Message, markAsRead: Boolean = false): Single<String> {
|
fun getMessagesContentDetails(message: Message, markAsRead: Boolean = false): Single<Pair<String, List<MessageAttachment>>> {
|
||||||
return sdk.getMessageContent(message.messageId, message.folderId, markAsRead, message.realId)
|
return sdk.getMessageDetails(message.messageId, message.folderId, markAsRead, message.realId).map { details ->
|
||||||
|
details.content to details.attachments.map {
|
||||||
|
MessageAttachment(
|
||||||
|
realId = it.id,
|
||||||
|
messageId = it.messageId,
|
||||||
|
oneDriveId = it.oneDriveId,
|
||||||
|
url = it.url,
|
||||||
|
filename = it.filename
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendMessage(subject: String, content: String, recipients: List<Recipient>): Single<SentMessage> {
|
fun sendMessage(subject: String, content: String, recipients: List<Recipient>): Single<SentMessage> {
|
||||||
|
@ -4,6 +4,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
|
|||||||
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||||
import io.github.wulkanowy.data.SdkHelper
|
import io.github.wulkanowy.data.SdkHelper
|
||||||
import io.github.wulkanowy.data.db.entities.Message
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
|
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
|
||||||
import io.github.wulkanowy.data.db.entities.Recipient
|
import io.github.wulkanowy.data.db.entities.Recipient
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
@ -11,7 +12,6 @@ import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED
|
|||||||
import io.github.wulkanowy.sdk.pojo.SentMessage
|
import io.github.wulkanowy.sdk.pojo.SentMessage
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import io.reactivex.Completable
|
import io.reactivex.Completable
|
||||||
import io.reactivex.Maybe
|
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
@ -48,30 +48,31 @@ class MessageRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMessage(student: Student, messageDbId: Long, markAsRead: Boolean = false): Single<Message> {
|
fun getMessage(student: Student, message: Message, markAsRead: Boolean = false): Single<MessageWithAttachment> {
|
||||||
return Single.just(sdkHelper.init(student))
|
return Single.just(sdkHelper.init(student))
|
||||||
.flatMap { _ ->
|
.flatMap { _ ->
|
||||||
local.getMessage(messageDbId)
|
local.getMessageWithAttachment(student, message)
|
||||||
.filter {
|
.filter {
|
||||||
it.content.isNotEmpty().also { status ->
|
it.message.content.isNotEmpty().also { status ->
|
||||||
Timber.d("Message content in db empty: ${!status}")
|
Timber.d("Message content in db empty: ${!status}")
|
||||||
} && !it.unread
|
} && !it.message.unread
|
||||||
}
|
}
|
||||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||||
.flatMap {
|
.flatMap {
|
||||||
if (it) local.getMessage(messageDbId)
|
if (it) local.getMessageWithAttachment(student, message)
|
||||||
else Single.error(UnknownHostException())
|
else Single.error(UnknownHostException())
|
||||||
}
|
}
|
||||||
.flatMap { dbMessage ->
|
.flatMap { dbMessage ->
|
||||||
remote.getMessagesContent(dbMessage, markAsRead).doOnSuccess {
|
remote.getMessagesContentDetails(dbMessage.message, markAsRead).doOnSuccess { (downloadedMessage, attachments) ->
|
||||||
local.updateMessages(listOf(dbMessage.copy(unread = !markAsRead).apply {
|
local.updateMessages(listOf(dbMessage.message.copy(unread = !markAsRead).apply {
|
||||||
id = dbMessage.id
|
id = dbMessage.message.id
|
||||||
content = content.ifBlank { it }
|
content = content.ifBlank { downloadedMessage }
|
||||||
}))
|
}))
|
||||||
Timber.d("Message $messageDbId with blank content: ${dbMessage.content.isBlank()}, marked as read")
|
local.saveMessageAttachments(attachments)
|
||||||
|
Timber.d("Message ${message.messageId} with blank content: ${dbMessage.message.content.isBlank()}, marked as read")
|
||||||
}
|
}
|
||||||
}.flatMap {
|
}.flatMap {
|
||||||
local.getMessage(messageDbId)
|
local.getMessageWithAttachment(student, message)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,9 @@ class MessageItem(val message: Message, private val noSubjectString: String) :
|
|||||||
text = message.date.toFormattedString()
|
text = message.date.toFormattedString()
|
||||||
setTypeface(null, style)
|
setTypeface(null, style)
|
||||||
}
|
}
|
||||||
|
with(messageItemAttachmentIcon) {
|
||||||
|
visibility = if (message.hasAttachments) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +59,8 @@ class MessageItem(val message: Message, private val noSubjectString: String) :
|
|||||||
return message.hashCode()
|
return message.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) :
|
||||||
|
FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||||
override val containerView: View
|
override val containerView: View
|
||||||
get() = contentView
|
get() = contentView
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.message.preview
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import io.github.wulkanowy.R
|
||||||
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
|
import io.github.wulkanowy.data.db.entities.MessageAttachment
|
||||||
|
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
|
||||||
|
import io.github.wulkanowy.data.repositories.message.MessageFolder
|
||||||
|
import io.github.wulkanowy.utils.openInternetBrowser
|
||||||
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
|
import kotlinx.android.synthetic.main.item_message_attachment.view.*
|
||||||
|
import kotlinx.android.synthetic.main.item_message_preview.view.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class MessagePreviewAdapter @Inject constructor() :
|
||||||
|
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
|
||||||
|
enum class ViewType(val id: Int) {
|
||||||
|
MESSAGE(1),
|
||||||
|
DIVIDER(2),
|
||||||
|
ATTACHMENT(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
var messageWithAttachment: MessageWithAttachment? = null
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
attachments = value?.attachments.orEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var attachments: List<MessageAttachment> = emptyList()
|
||||||
|
|
||||||
|
override fun getItemCount() = if (messageWithAttachment == null) 0 else attachments.size + 1 + if (attachments.isNotEmpty()) 1 else 0
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int) = when (position) {
|
||||||
|
0 -> ViewType.MESSAGE.id
|
||||||
|
1 -> ViewType.DIVIDER.id
|
||||||
|
else -> ViewType.ATTACHMENT.id
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
|
val inflater = LayoutInflater.from(parent.context)
|
||||||
|
|
||||||
|
return when (viewType) {
|
||||||
|
ViewType.MESSAGE.id -> MessageViewHolder(inflater.inflate(R.layout.item_message_preview, parent, false))
|
||||||
|
ViewType.DIVIDER.id -> DividerViewHolder(inflater.inflate(R.layout.item_message_divider, parent, false))
|
||||||
|
ViewType.ATTACHMENT.id -> AttachmentViewHolder(inflater.inflate(R.layout.item_message_attachment, parent, false))
|
||||||
|
else -> throw IllegalStateException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
|
when (holder) {
|
||||||
|
is MessageViewHolder -> bindMessage(holder.view, requireNotNull(messageWithAttachment).message)
|
||||||
|
is AttachmentViewHolder -> bindAttachment(holder.view, requireNotNull(messageWithAttachment).attachments[position - 2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
private fun bindMessage(view: View, message: Message) {
|
||||||
|
with(view) {
|
||||||
|
messagePreviewSubject.text = if (message.subject.isNotBlank()) message.subject else context.getString(R.string.message_no_subject)
|
||||||
|
messagePreviewDate.text = context.getString(R.string.message_date, message.date.toFormattedString("yyyy-MM-dd HH:mm:ss"))
|
||||||
|
messagePreviewContent.text = message.content
|
||||||
|
messagePreviewAuthor.text = if (message.folderId == MessageFolder.SENT.id) "${context.getString(R.string.message_to)} ${message.recipient}"
|
||||||
|
else "${context.getString(R.string.message_from)} ${message.sender}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindAttachment(view: View, attachment: MessageAttachment) {
|
||||||
|
with(view) {
|
||||||
|
messagePreviewAttachment.visibility = View.VISIBLE
|
||||||
|
messagePreviewAttachment.text = attachment.filename
|
||||||
|
setOnClickListener {
|
||||||
|
context.openInternetBrowser(attachment.url) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MessageViewHolder(val view: View) : RecyclerView.ViewHolder(view)
|
||||||
|
|
||||||
|
class DividerViewHolder(val view: View) : RecyclerView.ViewHolder(view)
|
||||||
|
|
||||||
|
class AttachmentViewHolder(val view: View) : RecyclerView.ViewHolder(view)
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
package io.github.wulkanowy.ui.modules.message.preview
|
package io.github.wulkanowy.ui.modules.message.preview
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
@ -10,8 +9,10 @@ import android.view.View
|
|||||||
import android.view.View.GONE
|
import android.view.View.GONE
|
||||||
import android.view.View.VISIBLE
|
import android.view.View.VISIBLE
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.Message
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
|
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
|
||||||
import io.github.wulkanowy.ui.base.BaseFragment
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
@ -25,6 +26,9 @@ class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.Titl
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var presenter: MessagePreviewPresenter
|
lateinit var presenter: MessagePreviewPresenter
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var previewAdapter: MessagePreviewAdapter
|
||||||
|
|
||||||
private var menuReplyButton: MenuItem? = null
|
private var menuReplyButton: MenuItem? = null
|
||||||
|
|
||||||
private var menuForwardButton: MenuItem? = null
|
private var menuForwardButton: MenuItem? = null
|
||||||
@ -34,18 +38,15 @@ class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.Titl
|
|||||||
override val titleStringId: Int
|
override val titleStringId: Int
|
||||||
get() = R.string.message_title
|
get() = R.string.message_title
|
||||||
|
|
||||||
override val noSubjectString: String
|
|
||||||
get() = getString(R.string.message_no_subject)
|
|
||||||
|
|
||||||
override val deleteMessageSuccessString: String
|
override val deleteMessageSuccessString: String
|
||||||
get() = getString(R.string.message_delete_success)
|
get() = getString(R.string.message_delete_success)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val MESSAGE_ID_KEY = "message_id"
|
const val MESSAGE_ID_KEY = "message_id"
|
||||||
|
|
||||||
fun newInstance(messageId: Long): MessagePreviewFragment {
|
fun newInstance(message: Message): MessagePreviewFragment {
|
||||||
return MessagePreviewFragment().apply {
|
return MessagePreviewFragment().apply {
|
||||||
arguments = Bundle().apply { putLong(MESSAGE_ID_KEY, messageId) }
|
arguments = Bundle().apply { putSerializable(MESSAGE_ID_KEY, message) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,11 +63,16 @@ class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.Titl
|
|||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
messageContainer = messagePreviewContainer
|
messageContainer = messagePreviewContainer
|
||||||
presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getLong(MESSAGE_ID_KEY) ?: 0L)
|
presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getSerializable(MESSAGE_ID_KEY) as Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initView() {
|
override fun initView() {
|
||||||
messagePreviewErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
messagePreviewErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
|
|
||||||
|
with(messagePreviewRecycler) {
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
adapter = previewAdapter
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
@ -86,26 +92,11 @@ class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.Titl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setSubject(subject: String) {
|
override fun setMessageWithAttachment(item: MessageWithAttachment) {
|
||||||
messagePreviewSubject.text = subject
|
with(previewAdapter) {
|
||||||
|
messageWithAttachment = item
|
||||||
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
|
||||||
override fun setRecipient(recipient: String) {
|
|
||||||
messagePreviewAuthor.text = "${getString(R.string.message_to)} $recipient"
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
|
||||||
override fun setSender(sender: String) {
|
|
||||||
messagePreviewAuthor.text = "${getString(R.string.message_from)} $sender"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setDate(date: String) {
|
|
||||||
messagePreviewDate.text = getString(R.string.message_date, date)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setContent(content: String) {
|
|
||||||
messagePreviewContent.text = content
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showProgress(show: Boolean) {
|
override fun showProgress(show: Boolean) {
|
||||||
@ -113,7 +104,7 @@ class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.Titl
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showContent(show: Boolean) {
|
override fun showContent(show: Boolean) {
|
||||||
messagePreviewContentContainer.visibility = if (show) VISIBLE else GONE
|
messagePreviewRecycler.visibility = if (show) VISIBLE else GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showOptions(show: Boolean) {
|
override fun showOptions(show: Boolean) {
|
||||||
@ -160,7 +151,7 @@ class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.Titl
|
|||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
outState.putLong(MESSAGE_ID_KEY, presenter.messageId)
|
outState.putSerializable(MESSAGE_ID_KEY, presenter.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
package io.github.wulkanowy.ui.modules.message.preview
|
package io.github.wulkanowy.ui.modules.message.preview
|
||||||
|
|
||||||
import io.github.wulkanowy.data.db.entities.Message
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
import io.github.wulkanowy.data.repositories.message.MessageFolder
|
|
||||||
import io.github.wulkanowy.data.repositories.message.MessageRepository
|
import io.github.wulkanowy.data.repositories.message.MessageRepository
|
||||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||||
import io.github.wulkanowy.ui.base.BasePresenter
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||||
import io.github.wulkanowy.utils.SchedulersProvider
|
import io.github.wulkanowy.utils.SchedulersProvider
|
||||||
import io.github.wulkanowy.utils.toFormattedString
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -20,61 +18,51 @@ class MessagePreviewPresenter @Inject constructor(
|
|||||||
private val analytics: FirebaseAnalyticsHelper
|
private val analytics: FirebaseAnalyticsHelper
|
||||||
) : BasePresenter<MessagePreviewView>(errorHandler, studentRepository, schedulers) {
|
) : BasePresenter<MessagePreviewView>(errorHandler, studentRepository, schedulers) {
|
||||||
|
|
||||||
var messageId = 0L
|
var message: Message? = null
|
||||||
|
|
||||||
private var message: Message? = null
|
|
||||||
|
|
||||||
private lateinit var lastError: Throwable
|
private lateinit var lastError: Throwable
|
||||||
|
|
||||||
private var retryCallback: () -> Unit = {}
|
private var retryCallback: () -> Unit = {}
|
||||||
|
|
||||||
fun onAttachView(view: MessagePreviewView, id: Long) {
|
fun onAttachView(view: MessagePreviewView, message: Message) {
|
||||||
super.onAttachView(view)
|
super.onAttachView(view)
|
||||||
view.initView()
|
view.initView()
|
||||||
errorHandler.showErrorMessage = ::showErrorViewOnError
|
errorHandler.showErrorMessage = ::showErrorViewOnError
|
||||||
loadData(id)
|
loadData(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onMessageLoadRetry() {
|
private fun onMessageLoadRetry(message: Message) {
|
||||||
view?.run {
|
view?.run {
|
||||||
showErrorView(false)
|
showErrorView(false)
|
||||||
showProgress(true)
|
showProgress(true)
|
||||||
}
|
}
|
||||||
loadData(messageId)
|
loadData(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onDetailsClick() {
|
fun onDetailsClick() {
|
||||||
view?.showErrorDetailsDialog(lastError)
|
view?.showErrorDetailsDialog(lastError)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadData(id: Long) {
|
private fun loadData(message: Message) {
|
||||||
Timber.i("Loading message $id preview started")
|
Timber.i("Loading message ${message.messageId} preview started")
|
||||||
messageId = id
|
|
||||||
disposable.apply {
|
disposable.apply {
|
||||||
clear()
|
clear()
|
||||||
add(studentRepository.getCurrentStudent()
|
add(studentRepository.getCurrentStudent()
|
||||||
.flatMap { messageRepository.getMessage(it, messageId, true) }
|
.flatMap { messageRepository.getMessage(it, message, true) }
|
||||||
.subscribeOn(schedulers.backgroundThread)
|
.subscribeOn(schedulers.backgroundThread)
|
||||||
.observeOn(schedulers.mainThread)
|
.observeOn(schedulers.mainThread)
|
||||||
.doFinally { view?.showProgress(false) }
|
.doFinally { view?.showProgress(false) }
|
||||||
.subscribe({ message ->
|
.subscribe({ message ->
|
||||||
Timber.i("Loading message $id preview result: Success ")
|
Timber.i("Loading message ${message.message.messageId} preview result: Success ")
|
||||||
this@MessagePreviewPresenter.message = message
|
this@MessagePreviewPresenter.message = message.message
|
||||||
view?.run {
|
view?.apply {
|
||||||
message.let {
|
setMessageWithAttachment(message)
|
||||||
setSubject(if (it.subject.isNotBlank()) it.subject else noSubjectString)
|
|
||||||
setDate(it.date.toFormattedString("yyyy-MM-dd HH:mm:ss"))
|
|
||||||
setContent(it.content)
|
|
||||||
initOptions()
|
initOptions()
|
||||||
|
|
||||||
if (it.folderId == MessageFolder.SENT.id) setRecipient(it.recipient)
|
|
||||||
else setSender(it.sender)
|
|
||||||
}
|
}
|
||||||
}
|
analytics.logEvent("load_message_preview", "length" to message.message.content.length)
|
||||||
analytics.logEvent("load_message_preview", "length" to message.content.length)
|
|
||||||
}) {
|
}) {
|
||||||
Timber.i("Loading message $id preview result: An exception occurred ")
|
Timber.i("Loading message ${message.messageId} preview result: An exception occurred ")
|
||||||
retryCallback = { onMessageLoadRetry() }
|
retryCallback = { onMessageLoadRetry(message) }
|
||||||
errorHandler.dispatch(it)
|
errorHandler.dispatch(it)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,16 @@
|
|||||||
package io.github.wulkanowy.ui.modules.message.preview
|
package io.github.wulkanowy.ui.modules.message.preview
|
||||||
|
|
||||||
import io.github.wulkanowy.data.db.entities.Message
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
|
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
|
||||||
import io.github.wulkanowy.ui.base.BaseView
|
import io.github.wulkanowy.ui.base.BaseView
|
||||||
|
|
||||||
interface MessagePreviewView : BaseView {
|
interface MessagePreviewView : BaseView {
|
||||||
|
|
||||||
val noSubjectString: String
|
|
||||||
|
|
||||||
val deleteMessageSuccessString: String
|
val deleteMessageSuccessString: String
|
||||||
|
|
||||||
fun initView()
|
fun initView()
|
||||||
|
|
||||||
fun setSubject(subject: String)
|
fun setMessageWithAttachment(item: MessageWithAttachment)
|
||||||
|
|
||||||
fun setRecipient(recipient: String)
|
|
||||||
|
|
||||||
fun setSender(sender: String)
|
|
||||||
|
|
||||||
fun setDate(date: String)
|
|
||||||
|
|
||||||
fun setContent(content: String)
|
|
||||||
|
|
||||||
fun showProgress(show: Boolean)
|
fun showProgress(show: Boolean)
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
|
|||||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
import io.github.wulkanowy.data.repositories.message.MessageFolder
|
import io.github.wulkanowy.data.repositories.message.MessageFolder
|
||||||
import io.github.wulkanowy.ui.base.BaseFragment
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
@ -116,8 +117,8 @@ class MessageTabFragment : BaseFragment(), MessageTabView {
|
|||||||
messageTabSwipe.isRefreshing = show
|
messageTabSwipe.isRefreshing = show
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openMessage(messageId: Long) {
|
override fun openMessage(message: Message) {
|
||||||
(activity as? MainActivity)?.pushView(MessagePreviewFragment.newInstance(messageId))
|
(activity as? MainActivity)?.pushView(MessagePreviewFragment.newInstance(message))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun notifyParentDataLoaded() {
|
override fun notifyParentDataLoaded() {
|
||||||
|
@ -62,7 +62,7 @@ class MessageTabPresenter @Inject constructor(
|
|||||||
if (item is MessageItem) {
|
if (item is MessageItem) {
|
||||||
Timber.i("Select message ${item.message.id} item")
|
Timber.i("Select message ${item.message.id} item")
|
||||||
view?.run {
|
view?.run {
|
||||||
openMessage(item.message.id)
|
openMessage(item.message)
|
||||||
if (item.message.unread) {
|
if (item.message.unread) {
|
||||||
item.message.unread = false
|
item.message.unread = false
|
||||||
updateItem(item)
|
updateItem(item)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package io.github.wulkanowy.ui.modules.message.tab
|
package io.github.wulkanowy.ui.modules.message.tab
|
||||||
|
|
||||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||||
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
import io.github.wulkanowy.ui.base.BaseView
|
import io.github.wulkanowy.ui.base.BaseView
|
||||||
import io.github.wulkanowy.ui.modules.message.MessageItem
|
import io.github.wulkanowy.ui.modules.message.MessageItem
|
||||||
|
|
||||||
@ -32,7 +33,7 @@ interface MessageTabView : BaseView {
|
|||||||
|
|
||||||
fun showRefresh(show: Boolean)
|
fun showRefresh(show: Boolean)
|
||||||
|
|
||||||
fun openMessage(messageId: Long)
|
fun openMessage(message: Message)
|
||||||
|
|
||||||
fun notifyParentDataLoaded()
|
fun notifyParentDataLoaded()
|
||||||
}
|
}
|
||||||
|
9
app/src/main/res/drawable/ic_attachment.xml
Normal file
9
app/src/main/res/drawable/ic_attachment.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M2,12.5C2,9.46 4.46,7 7.5,7H18c2.21,0 4,1.79 4,4s-1.79,4 -4,4H9.5C8.12,15 7,13.88 7,12.5S8.12,10 9.5,10H17v2H9.41c-0.55,0 -0.55,1 0,1H18c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2H7.5C5.57,9 4,10.57 4,12.5S5.57,16 7.5,16H17v2H7.5C4.46,18 2,15.54 2,12.5z"/>
|
||||||
|
</vector>
|
@ -5,55 +5,12 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/messagePreviewRecycler"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
tools:itemCount="1"
|
||||||
<LinearLayout
|
tools:listitem="@layout/item_message_preview" />
|
||||||
android:id="@+id/messagePreviewContentContainer"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/messagePreviewSubject"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="15dp"
|
|
||||||
android:lineSpacingMultiplier="1.2"
|
|
||||||
android:textSize="22sp"
|
|
||||||
tools:text="@tools:sample/lorem" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/messagePreviewAuthor"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="4dp"
|
|
||||||
android:textColor="?android:textColorSecondary"
|
|
||||||
android:textSize="15sp"
|
|
||||||
tools:text="@tools:sample/full_names" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/messagePreviewDate"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="15dp"
|
|
||||||
android:textColor="?android:textColorSecondary"
|
|
||||||
android:textSize="15sp"
|
|
||||||
tools:text="@tools:sample/date/ddmmyy" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/messagePreviewContent"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:autoLink="web"
|
|
||||||
android:lineSpacingMultiplier="1.2"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
tools:text="@tools:sample/lorem" />
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/messagePreviewError"
|
android:id="@+id/messagePreviewError"
|
||||||
@ -95,7 +52,6 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_marginRight="8dp"
|
|
||||||
android:text="@string/all_details" />
|
android:text="@string/all_details" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/relativeLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?selectableItemBackground"
|
android:background="?selectableItemBackground"
|
||||||
@ -11,41 +13,53 @@
|
|||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/messageItemAuthor"
|
android:id="@+id/messageItemAuthor"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_marginEnd="10dp"
|
||||||
android:layout_marginEnd="40dp"
|
android:layout_marginRight="10dp"
|
||||||
android:layout_marginRight="40dp"
|
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="1"
|
android:singleLine="true"
|
||||||
android:textSize="15sp"
|
android:textSize="15sp"
|
||||||
tools:text="@tools:sample/full_names" />
|
app:layout_constraintEnd_toStartOf="@+id/messageItemDate"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="@tools:sample/lorem/random" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/messageItemDate"
|
android:id="@+id/messageItemDate"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:layout_marginStart="10dp"
|
|
||||||
android:layout_marginLeft="10dp"
|
|
||||||
android:layout_toEndOf="@id/messageItemAuthor"
|
|
||||||
android:layout_toRightOf="@id/messageItemAuthor"
|
|
||||||
android:gravity="end"
|
android:gravity="end"
|
||||||
android:textSize="13sp"
|
android:textSize="13sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="@tools:sample/date/ddmmyy" />
|
tools:text="@tools:sample/date/ddmmyy" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/messageItemSubject"
|
android:id="@+id/messageItemSubject"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@+id/messageItemAuthor"
|
|
||||||
android:layout_alignStart="@id/messageItemAuthor"
|
|
||||||
android:layout_alignLeft="@id/messageItemAuthor"
|
|
||||||
android:layout_marginTop="5dp"
|
android:layout_marginTop="5dp"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="1"
|
android:singleLine="true"
|
||||||
android:textColor="?android:textColorSecondary"
|
android:textColor="?android:textColorSecondary"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/messageItemAttachmentIcon"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/messageItemAuthor"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/messageItemAuthor"
|
||||||
|
app:layout_goneMarginEnd="0dp"
|
||||||
tools:text="@tools:sample/lorem/random" />
|
tools:text="@tools:sample/lorem/random" />
|
||||||
</RelativeLayout>
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/messageItemAttachmentIcon"
|
||||||
|
android:layout_width="16dp"
|
||||||
|
android:layout_height="16dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/messageItemSubject"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/messageItemDate"
|
||||||
|
app:srcCompat="@drawable/ic_attachment"
|
||||||
|
app:tint="?colorOnBackground"
|
||||||
|
tools:ignore="ContentDescription"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
22
app/src/main/res/layout/item_message_attachment.xml
Normal file
22
app/src/main/res/layout/item_message_attachment.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingBottom="10dp">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/messagePreviewAttachment"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:drawablePadding="10dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:drawableStartCompat="@drawable/ic_attachment"
|
||||||
|
app:drawableTint="?colorOnBackground"
|
||||||
|
tools:text="@tools:sample/lorem"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
</LinearLayout>
|
6
app/src/main/res/layout/item_message_divider.xml
Normal file
6
app/src/main/res/layout/item_message_divider.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="2dp"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:background="?android:attr/listDivider" />
|
45
app/src/main/res/layout/item_message_preview.xml
Normal file
45
app/src/main/res/layout/item_message_preview.xml
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/messagePreviewContentContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/messagePreviewSubject"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="15dp"
|
||||||
|
android:lineSpacingMultiplier="1.2"
|
||||||
|
android:textSize="22sp"
|
||||||
|
tools:text="@tools:sample/lorem" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/messagePreviewAuthor"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:textColor="?android:textColorSecondary"
|
||||||
|
android:textSize="15sp"
|
||||||
|
tools:text="@tools:sample/full_names" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/messagePreviewDate"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="15dp"
|
||||||
|
android:textColor="?android:textColorSecondary"
|
||||||
|
android:textSize="15sp"
|
||||||
|
tools:text="@tools:sample/date/ddmmyy" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/messagePreviewContent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:autoLink="web"
|
||||||
|
android:lineSpacingMultiplier="1.2"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
tools:text="@tools:sample/lorem/random" />
|
||||||
|
</LinearLayout>
|
@ -4,6 +4,7 @@ import androidx.room.EmptyResultSetException
|
|||||||
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||||
import io.github.wulkanowy.data.SdkHelper
|
import io.github.wulkanowy.data.SdkHelper
|
||||||
import io.github.wulkanowy.data.db.entities.Message
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
|
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.repositories.UnitTestInternetObservingStrategy
|
import io.github.wulkanowy.data.repositories.UnitTestInternetObservingStrategy
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
@ -47,63 +48,70 @@ class MessageRepositoryTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `throw error when message is not in the db`() {
|
fun `throw error when message is not in the db`() {
|
||||||
`when`(local.getMessage(123)).thenReturn(Single.error(EmptyResultSetException("No message in database")))
|
val testMessage = Message(1, 1, 1, "", 1, "", "", "", now(), 1, false, 1, 1, false, false)
|
||||||
|
`when`(local.getMessageWithAttachment(student, testMessage)).thenReturn(Single.error(EmptyResultSetException("No message in database")))
|
||||||
|
|
||||||
val message = repo.getMessage(student, 123)
|
val message = repo.getMessage(student, testMessage)
|
||||||
val messageObserver = TestObserver<Message>()
|
val messageObserver = TestObserver<MessageWithAttachment>()
|
||||||
message.subscribe(messageObserver)
|
message.subscribe(messageObserver)
|
||||||
messageObserver.assertError(EmptyResultSetException::class.java)
|
messageObserver.assertError(EmptyResultSetException::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `get message when content already in db`() {
|
fun `get message when content already in db`() {
|
||||||
`when`(local.getMessage(123)).thenReturn(Single.just(
|
val testMessage = Message(1, 1, 123, "", 1, "", "", "Test", now(), 1, false, 1, 1, false, false)
|
||||||
Message(1, 1, 123, "", 1, "", "", "Test", now(), 1, false, 1, 1, false)
|
val messageWithAttachment = MessageWithAttachment(testMessage, emptyList())
|
||||||
))
|
|
||||||
|
|
||||||
val message = repo.getMessage(student, 123).blockingGet()
|
`when`(local.getMessageWithAttachment(student, testMessage)).thenReturn(Single.just(messageWithAttachment))
|
||||||
|
|
||||||
assertEquals("Test", message.content)
|
val message = repo.getMessage(student, testMessage).blockingGet()
|
||||||
|
|
||||||
|
assertEquals("Test", message.message.content)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `get message when content in db is empty`() {
|
fun `get message when content in db is empty`() {
|
||||||
val testMessage = Message(1, 1, 123, "", 1, "", "", "", now(), 1, true, 1, 1, false)
|
val testMessage = Message(1, 1, 123, "", 1, "", "", "", now(), 1, true, 1, 1, false, false)
|
||||||
val testMessageWithContent = testMessage.copy(content = "Test")
|
val testMessageWithContent = testMessage.copy(content = "Test")
|
||||||
|
|
||||||
`when`(local.getMessage(123))
|
val mWa = MessageWithAttachment(testMessage, emptyList())
|
||||||
.thenReturn(Single.just(testMessage))
|
val mWaWithContent = MessageWithAttachment(testMessageWithContent, emptyList())
|
||||||
.thenReturn(Single.just(testMessageWithContent))
|
|
||||||
`when`(remote.getMessagesContent(testMessageWithContent)).thenReturn(Single.just("Test"))
|
|
||||||
|
|
||||||
val message = repo.getMessage(student, 123).blockingGet()
|
`when`(local.getMessageWithAttachment(student, testMessage))
|
||||||
|
.thenReturn(Single.just(mWa))
|
||||||
|
.thenReturn(Single.just(mWaWithContent))
|
||||||
|
`when`(remote.getMessagesContentDetails(testMessageWithContent)).thenReturn(Single.just("Test" to emptyList()))
|
||||||
|
|
||||||
assertEquals("Test", message.content)
|
val message = repo.getMessage(student, testMessage).blockingGet()
|
||||||
|
|
||||||
|
assertEquals("Test", message.message.content)
|
||||||
verify(local).updateMessages(listOf(testMessageWithContent))
|
verify(local).updateMessages(listOf(testMessageWithContent))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `get message when content in db is empty and there is no internet connection`() {
|
fun `get message when content in db is empty and there is no internet connection`() {
|
||||||
val testMessage = Message(1, 1, 123, "", 1, "", "", "", now(), 1, false, 1, 1, false)
|
val testMessage = Message(1, 1, 123, "", 1, "", "", "", now(), 1, false, 1, 1, false, false)
|
||||||
|
val messageWithAttachment = MessageWithAttachment(testMessage, emptyList())
|
||||||
|
|
||||||
testObservingStrategy.isInternetConnection = false
|
testObservingStrategy.isInternetConnection = false
|
||||||
`when`(local.getMessage(123)).thenReturn(Single.just(testMessage))
|
`when`(local.getMessageWithAttachment(student, testMessage)).thenReturn(Single.just(messageWithAttachment))
|
||||||
|
|
||||||
val message = repo.getMessage(student, 123)
|
val message = repo.getMessage(student, testMessage)
|
||||||
val messageObserver = TestObserver<Message>()
|
val messageObserver = TestObserver<MessageWithAttachment>()
|
||||||
message.subscribe(messageObserver)
|
message.subscribe(messageObserver)
|
||||||
messageObserver.assertError(UnknownHostException::class.java)
|
messageObserver.assertError(UnknownHostException::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `get message when content in db is empty, unread and there is no internet connection`() {
|
fun `get message when content in db is empty, unread and there is no internet connection`() {
|
||||||
val testMessage = Message(1, 1, 123, "", 1, "", "", "", now(), 1, true, 1, 1, false)
|
val testMessage = Message(1, 1, 123, "", 1, "", "", "", now(), 1, true, 1, 1, false, false)
|
||||||
|
val messageWithAttachment = MessageWithAttachment(testMessage, emptyList())
|
||||||
|
|
||||||
testObservingStrategy.isInternetConnection = false
|
testObservingStrategy.isInternetConnection = false
|
||||||
`when`(local.getMessage(123)).thenReturn(Single.just(testMessage))
|
`when`(local.getMessageWithAttachment(student, testMessage)).thenReturn(Single.just(messageWithAttachment))
|
||||||
|
|
||||||
val message = repo.getMessage(student, 123)
|
val message = repo.getMessage(student, testMessage)
|
||||||
val messageObserver = TestObserver<Message>()
|
val messageObserver = TestObserver<MessageWithAttachment>()
|
||||||
message.subscribe(messageObserver)
|
message.subscribe(messageObserver)
|
||||||
messageObserver.assertError(UnknownHostException::class.java)
|
messageObserver.assertError(UnknownHostException::class.java)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user