Add message attachments (#734)

This commit is contained in:
Mikołaj Pich 2020-04-02 20:27:53 +02:00 committed by GitHub
parent da357775ff
commit 502a98b70a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 2157 additions and 189 deletions

View File

@ -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"

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

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

View File

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

View File

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

View File

@ -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

View File

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

View File

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

View File

@ -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>> {

View File

@ -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> {

View File

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

View File

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

View File

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

View File

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

View File

@ -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) initOptions()
setDate(it.date.toFormattedString("yyyy-MM-dd HH:mm:ss"))
setContent(it.content)
initOptions()
if (it.folderId == MessageFolder.SENT.id) setRecipient(it.recipient)
else setSender(it.sender)
}
} }
analytics.logEvent("load_message_preview", "length" to message.content.length) analytics.logEvent("load_message_preview", "length" to message.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)
}) })
} }

View File

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

View File

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

View File

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

View File

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

View 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>

View File

@ -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

View File

@ -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>

View 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>

View 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" />

View 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>

View File

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