diff --git a/data/src/main/java/receiver/RemoteMessagingReceiver.kt b/data/src/main/java/receiver/RemoteMessagingReceiver.kt index a64f1154..698180b1 100644 --- a/data/src/main/java/receiver/RemoteMessagingReceiver.kt +++ b/data/src/main/java/receiver/RemoteMessagingReceiver.kt @@ -26,11 +26,13 @@ import compat.SubscriptionManagerCompat import dagger.android.AndroidInjection import interactor.MarkRead import interactor.SendMessage +import repository.ConversationRepository import repository.MessageRepository import javax.inject.Inject class RemoteMessagingReceiver : BroadcastReceiver() { + @Inject lateinit var conversationRepo: ConversationRepository @Inject lateinit var markRead: MarkRead @Inject lateinit var messageRepo: MessageRepository @Inject lateinit var sendMessage: SendMessage @@ -48,7 +50,7 @@ class RemoteMessagingReceiver : BroadcastReceiver() { val lastMessage = messageRepo.getMessages(threadId).lastOrNull() val subId = subscriptionManager.activeSubscriptionInfoList.firstOrNull { it.subscriptionId == lastMessage?.subId }?.subscriptionId ?: -1 - val addresses = messageRepo.getConversation(threadId)?.recipients?.map { it.address } ?: return + val addresses = conversationRepo.getConversation(threadId)?.recipients?.map { it.address } ?: return val pendingRepository = goAsync() sendMessage.execute(SendMessage.Params(subId, threadId, addresses, body)) { pendingRepository.finish() } diff --git a/data/src/main/java/repository/ConversationRepositoryImpl.kt b/data/src/main/java/repository/ConversationRepositoryImpl.kt new file mode 100644 index 00000000..8cb377c0 --- /dev/null +++ b/data/src/main/java/repository/ConversationRepositoryImpl.kt @@ -0,0 +1,275 @@ +package repository + +import android.content.ContentUris +import android.content.Context +import android.provider.Telephony +import android.telephony.PhoneNumberUtils +import compat.TelephonyCompat +import filter.ConversationFilter +import io.realm.Case +import io.realm.Realm +import io.realm.RealmResults +import io.realm.Sort +import mapper.CursorToConversation +import mapper.CursorToRecipient +import model.Contact +import model.Conversation +import model.Message +import model.SearchResult +import util.extensions.anyOf +import util.extensions.map +import util.tryOrNull +import javax.inject.Inject + +class ConversationRepositoryImpl @Inject constructor( + private val context: Context, + private val conversationFilter: ConversationFilter, + private val cursorToConversation: CursorToConversation, + private val cursorToRecipient: CursorToRecipient +) : ConversationRepository { + + override fun getConversations(archived: Boolean): RealmResults { + return Realm.getDefaultInstance() + .where(Conversation::class.java) + .notEqualTo("id", 0L) + .greaterThan("count", 0) + .equalTo("archived", archived) + .equalTo("blocked", false) + .isNotEmpty("recipients") + .sort("date", Sort.DESCENDING) + .findAllAsync() + } + + override fun getConversationsSnapshot(): List { + val realm = Realm.getDefaultInstance() + return realm.copyFromRealm(realm.where(Conversation::class.java) + .notEqualTo("id", 0L) + .greaterThan("count", 0) + .equalTo("archived", false) + .equalTo("blocked", false) + .isNotEmpty("recipients") + .sort("date", Sort.DESCENDING) + .findAll()) + } + + override fun setConversationName(id: Long, name: String) { + Realm.getDefaultInstance().use { realm -> + realm.executeTransaction { + realm.where(Conversation::class.java) + .equalTo("id", id) + .findFirst() + ?.name = name + } + } + } + + override fun searchConversations(query: String): List { + val conversations = getConversationsSnapshot() + + val messagesByConversation = Realm.getDefaultInstance() + .where(Message::class.java) + .contains("body", query, Case.INSENSITIVE) + .findAll() + .groupBy { message -> message.threadId } + .filter { (threadId, _) -> conversations.firstOrNull { it.id == threadId } != null } + .map { (threadId, messages) -> Pair(conversations.first { it.id == threadId }, messages.size) } + .map { (conversation, messages) -> SearchResult(query, conversation, messages) } + .sortedByDescending { result -> result.messages } + + return conversations + .filter { conversation -> conversationFilter.filter(conversation, query) } + .map { conversation -> SearchResult(query, conversation, 0) } + .plus(messagesByConversation) + } + + override fun getBlockedConversations(): RealmResults { + return Realm.getDefaultInstance() + .where(Conversation::class.java) + .equalTo("blocked", true) + .findAllAsync() + } + + override fun getConversationAsync(threadId: Long): Conversation { + return Realm.getDefaultInstance() + .where(Conversation::class.java) + .equalTo("id", threadId) + .findFirstAsync() + } + + override fun getConversation(threadId: Long): Conversation? { + return Realm.getDefaultInstance() + .where(Conversation::class.java) + .equalTo("id", threadId) + .findFirst() + } + + override fun getOrCreateConversation(threadId: Long): Conversation? { + return getConversation(threadId) ?: getConversationFromCp(threadId) + } + + override fun getOrCreateConversation(address: String): Conversation? { + return getOrCreateConversation(listOf(address)) + } + + override fun getOrCreateConversation(addresses: List): Conversation? { + return tryOrNull { TelephonyCompat.getOrCreateThreadId(context, addresses.toSet()) } + ?.takeIf { threadId -> threadId != 0L } + ?.let { threadId -> + var conversation = getConversation(threadId) + if (conversation != null) conversation = Realm.getDefaultInstance().copyFromRealm(conversation) + + conversation ?: getConversationFromCp(threadId) + } + } + + override fun saveDraft(threadId: Long, draft: String) { + Realm.getDefaultInstance().use { realm -> + val conversation = realm.where(Conversation::class.java) + .equalTo("id", threadId) + .findFirst() + + conversation?.let { + realm.executeTransaction { + conversation.draft = draft + } + } + } + } + + override fun updateConversations(vararg threadIds: Long) { + Realm.getDefaultInstance().use { realm -> + realm.refresh() + + threadIds.forEach { threadId -> + val conversation = realm + .where(Conversation::class.java) + .equalTo("id", threadId) + .findFirst() ?: return + + val messages = realm + .where(Message::class.java) + .equalTo("threadId", threadId) + .sort("date", Sort.DESCENDING) + .findAll() + + val message = messages.firstOrNull() + + realm.executeTransaction { + conversation.count = messages.size + conversation.date = message?.date ?: 0 + conversation.snippet = message?.getSummary() ?: "" + conversation.read = message?.read ?: true + conversation.me = message?.isMe() ?: false + } + } + } + } + + override fun markArchived(vararg threadIds: Long) { + Realm.getDefaultInstance().use { realm -> + val conversations = realm.where(Conversation::class.java) + .anyOf("id", threadIds) + .findAll() + + realm.executeTransaction { + conversations.forEach { it.archived = true } + } + } + } + + override fun markUnarchived(vararg threadIds: Long) { + Realm.getDefaultInstance().use { realm -> + val conversations = realm.where(Conversation::class.java) + .anyOf("id", threadIds) + .findAll() + + realm.executeTransaction { + conversations.forEach { it.archived = false } + } + } + } + + override fun markBlocked(vararg threadIds: Long) { + Realm.getDefaultInstance().use { realm -> + val conversations = realm.where(Conversation::class.java) + .anyOf("id", threadIds) + .findAll() + + realm.executeTransaction { + conversations.forEach { it.blocked = true } + } + } + } + + override fun markUnblocked(vararg threadIds: Long) { + Realm.getDefaultInstance().use { realm -> + val conversations = realm.where(Conversation::class.java) + .anyOf("id", threadIds) + .equalTo("archived", false) + .findAll() + + realm.executeTransaction { + conversations.forEach { it.blocked = false } + } + } + } + + override fun deleteConversations(vararg threadIds: Long) { + Realm.getDefaultInstance().use { realm -> + val conversation = realm.where(Conversation::class.java).anyOf("id", threadIds).findAll() + val messages = realm.where(Message::class.java).anyOf("threadId", threadIds).findAll() + + realm.executeTransaction { + conversation.deleteAllFromRealm() + messages.deleteAllFromRealm() + } + } + + threadIds.forEach { threadId -> + val uri = ContentUris.withAppendedId(Telephony.Threads.CONTENT_URI, threadId) + context.contentResolver.delete(uri, null, null) + } + } + + /** + * Returns a [Conversation] from the system SMS ContentProvider, based on the [threadId] + * + * It should be noted that even if we have a valid [threadId], that does not guarantee that + * we can return a [Conversation]. On some devices, the ContentProvider won't return the + * conversation unless it contains at least 1 message + */ + private fun getConversationFromCp(threadId: Long): Conversation? { + return cursorToConversation.getConversationCursor(threadId) + ?.takeIf { cursor -> cursor.moveToFirst() } + ?.use { cursor -> + val conversation = cursorToConversation.map(cursor) + + val realm = Realm.getDefaultInstance() + val contacts = realm.copyFromRealm(realm.where(Contact::class.java).findAll()) + + val recipients = conversation.recipients + .map { recipient -> recipient.id } + .map { id -> cursorToRecipient.getRecipientCursor(id) } + .mapNotNull { recipientCursor -> + // Map the recipient cursor to a list of recipients + recipientCursor?.use { recipientCursor.map { cursorToRecipient.map(recipientCursor) } } + } + .flatten() + .map { recipient -> + recipient.apply { + contact = contacts.firstOrNull { + it.numbers.any { PhoneNumberUtils.compare(recipient.address, it.address) } + } + } + } + + conversation.recipients.clear() + conversation.recipients.addAll(recipients) + realm.executeTransaction { it.insertOrUpdate(conversation) } + realm.close() + + conversation + } + } + +} \ No newline at end of file diff --git a/data/src/main/java/repository/MessageRepositoryImpl.kt b/data/src/main/java/repository/MessageRepositoryImpl.kt index a3fd0bc7..8d44d262 100644 --- a/data/src/main/java/repository/MessageRepositoryImpl.kt +++ b/data/src/main/java/repository/MessageRepositoryImpl.kt @@ -66,120 +66,10 @@ import javax.inject.Singleton class MessageRepositoryImpl @Inject constructor( private val context: Context, private val messageIds: KeyManager, - private val conversationFilter: ConversationFilter, - private val cursorToConversation: CursorToConversation, - private val cursorToRecipient: CursorToRecipient, + private val conversationRepo: ConversationRepository, private val imageRepository: ImageRepository, private val prefs: Preferences) : MessageRepository { - override fun getConversations(archived: Boolean): RealmResults { - return Realm.getDefaultInstance() - .where(Conversation::class.java) - .notEqualTo("id", 0L) - .greaterThan("count", 0) - .equalTo("archived", archived) - .equalTo("blocked", false) - .isNotEmpty("recipients") - .sort("date", Sort.DESCENDING) - .findAllAsync() - } - - override fun getConversationsSnapshot(): List { - val realm = Realm.getDefaultInstance() - return realm.copyFromRealm(realm.where(Conversation::class.java) - .notEqualTo("id", 0L) - .greaterThan("count", 0) - .equalTo("archived", false) - .equalTo("blocked", false) - .isNotEmpty("recipients") - .sort("date", Sort.DESCENDING) - .findAll()) - } - - override fun setConversationName(id: Long, name: String) { - Realm.getDefaultInstance().use { realm -> - realm.executeTransaction { - realm.where(Conversation::class.java) - .equalTo("id", id) - .findFirst() - ?.name = name - } - } - } - - override fun searchConversations(query: String): List { - val conversations = getConversationsSnapshot() - - val messagesByConversation = Realm.getDefaultInstance() - .where(Message::class.java) - .contains("body", query, Case.INSENSITIVE) - .findAll() - .groupBy { message -> message.threadId } - .filter { (threadId, _) -> conversations.firstOrNull { it.id == threadId } != null } - .map { (threadId, messages) -> Pair(conversations.first { it.id == threadId }, messages.size) } - .map { (conversation, messages) -> SearchResult(query, conversation, messages) } - .sortedByDescending { result -> result.messages } - - return conversations - .filter { conversation -> conversationFilter.filter(conversation, query) } - .map { conversation -> SearchResult(query, conversation, 0) } - .plus(messagesByConversation) - } - - override fun getBlockedConversations(): RealmResults { - return Realm.getDefaultInstance() - .where(Conversation::class.java) - .equalTo("blocked", true) - .findAllAsync() - } - - override fun getConversationAsync(threadId: Long): Conversation { - return Realm.getDefaultInstance() - .where(Conversation::class.java) - .equalTo("id", threadId) - .findFirstAsync() - } - - override fun getConversation(threadId: Long): Conversation? { - return Realm.getDefaultInstance() - .where(Conversation::class.java) - .equalTo("id", threadId) - .findFirst() - } - - override fun getOrCreateConversation(threadId: Long): Conversation? { - return getConversation(threadId) ?: getConversationFromCp(threadId) - } - - override fun getOrCreateConversation(address: String): Conversation? { - return getOrCreateConversation(listOf(address)) - } - - override fun getOrCreateConversation(addresses: List): Conversation? { - return tryOrNull { TelephonyCompat.getOrCreateThreadId(context, addresses.toSet()) } - ?.takeIf { threadId -> threadId != 0L } - ?.let { threadId -> - var conversation = getConversation(threadId) - if (conversation != null) conversation = Realm.getDefaultInstance().copyFromRealm(conversation) - - conversation ?: getConversationFromCp(threadId) - } - } - - override fun saveDraft(threadId: Long, draft: String) { - Realm.getDefaultInstance().use { realm -> - val conversation = realm.where(Conversation::class.java) - .equalTo("id", threadId) - .findFirst() - - conversation?.let { - realm.executeTransaction { - conversation.draft = draft - } - } - } - } - override fun getMessages(threadId: Long, query: String): RealmResults { return Realm.getDefaultInstance() .where(Message::class.java) @@ -256,84 +146,6 @@ class MessageRepositoryImpl @Inject constructor( .findAll() } - override fun updateConversations(vararg threadIds: Long) { - Realm.getDefaultInstance().use { realm -> - realm.refresh() - - threadIds.forEach { threadId -> - val conversation = realm - .where(Conversation::class.java) - .equalTo("id", threadId) - .findFirst() ?: return - - val messages = realm - .where(Message::class.java) - .equalTo("threadId", threadId) - .sort("date", Sort.DESCENDING) - .findAll() - - val message = messages.firstOrNull() - - realm.executeTransaction { - conversation.count = messages.size - conversation.date = message?.date ?: 0 - conversation.snippet = message?.getSummary() ?: "" - conversation.read = message?.read ?: true - conversation.me = message?.isMe() ?: false - } - } - } - } - - override fun markArchived(vararg threadIds: Long) { - Realm.getDefaultInstance().use { realm -> - val conversations = realm.where(Conversation::class.java) - .anyOf("id", threadIds) - .findAll() - - realm.executeTransaction { - conversations.forEach { it.archived = true } - } - } - } - - override fun markUnarchived(vararg threadIds: Long) { - Realm.getDefaultInstance().use { realm -> - val conversations = realm.where(Conversation::class.java) - .anyOf("id", threadIds) - .findAll() - - realm.executeTransaction { - conversations.forEach { it.archived = false } - } - } - } - - override fun markBlocked(vararg threadIds: Long) { - Realm.getDefaultInstance().use { realm -> - val conversations = realm.where(Conversation::class.java) - .anyOf("id", threadIds) - .findAll() - - realm.executeTransaction { - conversations.forEach { it.blocked = true } - } - } - } - - override fun markUnblocked(vararg threadIds: Long) { - Realm.getDefaultInstance().use { realm -> - val conversations = realm.where(Conversation::class.java) - .anyOf("id", threadIds) - .equalTo("archived", false) - .findAll() - - realm.executeTransaction { - conversations.forEach { it.blocked = false } - } - } - } - override fun markAllSeen() { val realm = Realm.getDefaultInstance() val messages = realm.where(Message::class.java).equalTo("seen", false).findAll() @@ -584,7 +396,7 @@ class MessageRepositoryImpl @Inject constructor( this.subId = subId id = messageIds.newId() - threadId = getOrCreateConversation(address)?.id ?: 0L + threadId = conversationRepo.getOrCreateConversation(address)?.id ?: 0L contentId = uri.lastPathSegment.toLong() boxId = Telephony.Sms.MESSAGE_TYPE_INBOX type = "sms" @@ -718,62 +530,4 @@ class MessageRepositoryImpl @Inject constructor( } } - override fun deleteConversations(vararg threadIds: Long) { - Realm.getDefaultInstance().use { realm -> - val conversation = realm.where(Conversation::class.java).anyOf("id", threadIds).findAll() - val messages = realm.where(Message::class.java).anyOf("threadId", threadIds).findAll() - - realm.executeTransaction { - conversation.deleteAllFromRealm() - messages.deleteAllFromRealm() - } - } - - threadIds.forEach { threadId -> - val uri = ContentUris.withAppendedId(Telephony.Threads.CONTENT_URI, threadId) - context.contentResolver.delete(uri, null, null) - } - } - - /** - * Returns a [Conversation] from the system SMS ContentProvider, based on the [threadId] - * - * It should be noted that even if we have a valid [threadId], that does not guarantee that - * we can return a [Conversation]. On some devices, the ContentProvider won't return the - * conversation unless it contains at least 1 message - */ - private fun getConversationFromCp(threadId: Long): Conversation? { - return cursorToConversation.getConversationCursor(threadId) - ?.takeIf { cursor -> cursor.moveToFirst() } - ?.use { cursor -> - val conversation = cursorToConversation.map(cursor) - - val realm = Realm.getDefaultInstance() - val contacts = realm.copyFromRealm(realm.where(Contact::class.java).findAll()) - - val recipients = conversation.recipients - .map { recipient -> recipient.id } - .map { id -> cursorToRecipient.getRecipientCursor(id) } - .mapNotNull { recipientCursor -> - // Map the recipient cursor to a list of recipients - recipientCursor?.use { recipientCursor.map { cursorToRecipient.map(recipientCursor) } } - } - .flatten() - .map { recipient -> - recipient.apply { - contact = contacts.firstOrNull { - it.numbers.any { PhoneNumberUtils.compare(recipient.address, it.address) } - } - } - } - - conversation.recipients.clear() - conversation.recipients.addAll(recipients) - realm.executeTransaction { it.insertOrUpdate(conversation) } - realm.close() - - conversation - } - } - } \ No newline at end of file diff --git a/data/src/main/java/repository/SyncRepositoryImpl.kt b/data/src/main/java/repository/SyncRepositoryImpl.kt index b489dce3..cb32617a 100644 --- a/data/src/main/java/repository/SyncRepositoryImpl.kt +++ b/data/src/main/java/repository/SyncRepositoryImpl.kt @@ -47,6 +47,7 @@ import javax.inject.Singleton @Singleton class SyncRepositoryImpl @Inject constructor( private val contentResolver: ContentResolver, + private val conversationRepo: ConversationRepository, private val messageRepo: MessageRepository, private val cursorToConversation: CursorToConversation, private val cursorToMessage: CursorToMessage, @@ -190,7 +191,7 @@ class SyncRepositoryImpl @Inject constructor( cursorToMessage.map(Pair(cursor, columnsMap)).apply { existingId?.let { this.id = it } - messageRepo.getOrCreateConversation(threadId) + conversationRepo.getOrCreateConversation(threadId) insertOrUpdate() } } diff --git a/data/src/main/java/service/HeadlessSmsSendService.kt b/data/src/main/java/service/HeadlessSmsSendService.kt index c4093b2f..64af9f6c 100644 --- a/data/src/main/java/service/HeadlessSmsSendService.kt +++ b/data/src/main/java/service/HeadlessSmsSendService.kt @@ -24,12 +24,12 @@ import android.net.Uri import android.telephony.TelephonyManager import dagger.android.AndroidInjection import interactor.SendMessage -import repository.MessageRepository +import repository.ConversationRepository import javax.inject.Inject class HeadlessSmsSendService : IntentService("HeadlessSmsSendService") { - @Inject lateinit var messageRepo: MessageRepository + @Inject lateinit var conversationRepo: ConversationRepository @Inject lateinit var sendMessage: SendMessage override fun onHandleIntent(intent: Intent?) { @@ -39,7 +39,7 @@ class HeadlessSmsSendService : IntentService("HeadlessSmsSendService") { intent.extras?.getString(Intent.EXTRA_TEXT)?.takeIf { it.isNotBlank() }?.let { body -> val intentUri = intent.data val recipients = getRecipients(intentUri).split(";") - val threadId = messageRepo.getOrCreateConversation(recipients)?.id ?: 0L + val threadId = conversationRepo.getOrCreateConversation(recipients)?.id ?: 0L sendMessage.execute(SendMessage.Params(-1, threadId, recipients, body)) } } diff --git a/domain/src/main/java/interactor/DeleteConversations.kt b/domain/src/main/java/interactor/DeleteConversations.kt index ba9bdec9..a5e7a5b5 100644 --- a/domain/src/main/java/interactor/DeleteConversations.kt +++ b/domain/src/main/java/interactor/DeleteConversations.kt @@ -19,17 +19,17 @@ package interactor import io.reactivex.Flowable -import repository.MessageRepository +import repository.ConversationRepository import javax.inject.Inject class DeleteConversations @Inject constructor( - private val messageRepo: MessageRepository, + private val conversationRepo: ConversationRepository, private val updateBadge: UpdateBadge ) : Interactor>() { override fun buildObservable(params: List): Flowable<*> { return Flowable.just(params.toLongArray()) - .doOnNext { threadIds -> messageRepo.deleteConversations(*threadIds) } + .doOnNext { threadIds -> conversationRepo.deleteConversations(*threadIds) } .flatMap { updateBadge.buildObservable(Unit) } // Update the badge } diff --git a/domain/src/main/java/interactor/DeleteMessages.kt b/domain/src/main/java/interactor/DeleteMessages.kt index 1c6b6353..56a5e6b1 100644 --- a/domain/src/main/java/interactor/DeleteMessages.kt +++ b/domain/src/main/java/interactor/DeleteMessages.kt @@ -19,10 +19,12 @@ package interactor import io.reactivex.Flowable +import repository.ConversationRepository import repository.MessageRepository import javax.inject.Inject class DeleteMessages @Inject constructor( + private val conversationRepo: ConversationRepository, private val messageRepo: MessageRepository, private val updateBadge: UpdateBadge ) : Interactor() { @@ -32,7 +34,7 @@ class DeleteMessages @Inject constructor( override fun buildObservable(params: Params): Flowable<*> { return Flowable.just(params.messageIds.toLongArray()) .doOnNext { messageIds -> messageRepo.deleteMessages(*messageIds) } // Delete the messages - .doOnNext { params.threadId?.let { messageRepo.updateConversations(it) } } // Update the conversation + .doOnNext { params.threadId?.let { conversationRepo.updateConversations(it) } } // Update the conversation .flatMap { updateBadge.buildObservable(Unit) } // Update the badge } diff --git a/domain/src/main/java/interactor/MarkArchived.kt b/domain/src/main/java/interactor/MarkArchived.kt index d8b38b9f..56aa4081 100644 --- a/domain/src/main/java/interactor/MarkArchived.kt +++ b/domain/src/main/java/interactor/MarkArchived.kt @@ -19,14 +19,14 @@ package interactor import io.reactivex.Flowable -import repository.MessageRepository +import repository.ConversationRepository import javax.inject.Inject -class MarkArchived @Inject constructor(private val messageRepo: MessageRepository) : Interactor>() { +class MarkArchived @Inject constructor(private val conversationRepo: ConversationRepository) : Interactor>() { override fun buildObservable(params: List): Flowable<*> { return Flowable.just(params.toLongArray()) - .doOnNext { threadIds -> messageRepo.markArchived(*threadIds) } + .doOnNext { threadIds -> conversationRepo.markArchived(*threadIds) } } } \ No newline at end of file diff --git a/domain/src/main/java/interactor/MarkBlocked.kt b/domain/src/main/java/interactor/MarkBlocked.kt index a1ff6220..8b2d694c 100644 --- a/domain/src/main/java/interactor/MarkBlocked.kt +++ b/domain/src/main/java/interactor/MarkBlocked.kt @@ -19,14 +19,14 @@ package interactor import io.reactivex.Flowable -import repository.MessageRepository +import repository.ConversationRepository import javax.inject.Inject -class MarkBlocked @Inject constructor(private val messageRepo: MessageRepository) : Interactor>() { +class MarkBlocked @Inject constructor(private val conversationRepo: ConversationRepository) : Interactor>() { override fun buildObservable(params: List): Flowable<*> { return Flowable.just(params.toLongArray()) - .doOnNext { threadIds -> messageRepo.markBlocked(*threadIds) } + .doOnNext { threadIds -> conversationRepo.markBlocked(*threadIds) } } } \ No newline at end of file diff --git a/domain/src/main/java/interactor/MarkRead.kt b/domain/src/main/java/interactor/MarkRead.kt index 4d2f10db..71867b0b 100644 --- a/domain/src/main/java/interactor/MarkRead.kt +++ b/domain/src/main/java/interactor/MarkRead.kt @@ -20,10 +20,12 @@ package interactor import io.reactivex.Flowable import manager.NotificationManager +import repository.ConversationRepository import repository.MessageRepository import javax.inject.Inject class MarkRead @Inject constructor( + private val conversationRepo: ConversationRepository, private val messageRepo: MessageRepository, private val notificationManager: NotificationManager, private val updateBadge: UpdateBadge @@ -32,7 +34,7 @@ class MarkRead @Inject constructor( override fun buildObservable(params: List): Flowable<*> { return Flowable.just(params.toLongArray()) .doOnNext { threadIds -> messageRepo.markRead(*threadIds) } - .doOnNext { threadIds -> messageRepo.updateConversations(*threadIds) } // Update the conversation + .doOnNext { threadIds -> conversationRepo.updateConversations(*threadIds) } // Update the conversation .doOnNext { threadIds -> threadIds.forEach(notificationManager::update) } .flatMap { updateBadge.buildObservable(Unit) } // Update the badge } diff --git a/domain/src/main/java/interactor/MarkUnarchived.kt b/domain/src/main/java/interactor/MarkUnarchived.kt index d7ce0b8c..0f7395d2 100644 --- a/domain/src/main/java/interactor/MarkUnarchived.kt +++ b/domain/src/main/java/interactor/MarkUnarchived.kt @@ -19,14 +19,14 @@ package interactor import io.reactivex.Flowable -import repository.MessageRepository +import repository.ConversationRepository import javax.inject.Inject -class MarkUnarchived @Inject constructor(private val messageRepo: MessageRepository) : Interactor>() { +class MarkUnarchived @Inject constructor(private val conversationRepo: ConversationRepository) : Interactor>() { override fun buildObservable(params: List): Flowable<*> { return Flowable.just(params.toLongArray()) - .doOnNext { threadIds -> messageRepo.markUnarchived(*threadIds) } + .doOnNext { threadIds -> conversationRepo.markUnarchived(*threadIds) } } } \ No newline at end of file diff --git a/domain/src/main/java/interactor/MarkUnblocked.kt b/domain/src/main/java/interactor/MarkUnblocked.kt index ba7b5aec..85a567ae 100644 --- a/domain/src/main/java/interactor/MarkUnblocked.kt +++ b/domain/src/main/java/interactor/MarkUnblocked.kt @@ -19,14 +19,14 @@ package interactor import io.reactivex.Flowable -import repository.MessageRepository +import repository.ConversationRepository import javax.inject.Inject -class MarkUnblocked @Inject constructor(private val messageRepo: MessageRepository) : Interactor>() { +class MarkUnblocked @Inject constructor(private val conversationRepo: ConversationRepository) : Interactor>() { override fun buildObservable(params: List): Flowable<*> { return Flowable.just(params.toLongArray()) - .doOnNext { threadIds -> messageRepo.markUnblocked(*threadIds) } + .doOnNext { threadIds -> conversationRepo.markUnblocked(*threadIds) } } } \ No newline at end of file diff --git a/domain/src/main/java/interactor/ReceiveMms.kt b/domain/src/main/java/interactor/ReceiveMms.kt index 26c5e5f3..900a6e59 100644 --- a/domain/src/main/java/interactor/ReceiveMms.kt +++ b/domain/src/main/java/interactor/ReceiveMms.kt @@ -22,6 +22,7 @@ import android.net.Uri import io.reactivex.Flowable import manager.ExternalBlockingManager import manager.NotificationManager +import repository.ConversationRepository import repository.MessageRepository import repository.SyncRepository import util.extensions.mapNotNull @@ -29,6 +30,7 @@ import java.util.concurrent.TimeUnit import javax.inject.Inject class ReceiveMms @Inject constructor( + private val conversationRepo: ConversationRepository, private val externalBlockingManager: ExternalBlockingManager, private val syncManager: SyncRepository, private val messageRepo: MessageRepository, @@ -48,10 +50,10 @@ class ReceiveMms @Inject constructor( if (blocked) messageRepo.deleteMessages(message.id) } } - .doOnNext { message -> messageRepo.updateConversations(message.threadId) } // Update the conversation - .mapNotNull { message -> messageRepo.getOrCreateConversation(message.threadId) } // Map message to conversation + .doOnNext { message -> conversationRepo.updateConversations(message.threadId) } // Update the conversation + .mapNotNull { message -> conversationRepo.getOrCreateConversation(message.threadId) } // Map message to conversation .filter { conversation -> !conversation.blocked } // Don't notify for blocked conversations - .doOnNext { conversation -> if (conversation.archived) messageRepo.markUnarchived(conversation.id) } // Unarchive conversation if necessary + .doOnNext { conversation -> if (conversation.archived) conversationRepo.markUnarchived(conversation.id) } // Unarchive conversation if necessary .map { conversation -> conversation.id } // Map to the id because [delay] will put us on the wrong thread .delay(1, TimeUnit.SECONDS) // Wait one second before trying to notify, in case the foreground app marks it as read first .doOnNext { threadId -> notificationManager.update(threadId) } // Update the notification diff --git a/domain/src/main/java/interactor/ReceiveSms.kt b/domain/src/main/java/interactor/ReceiveSms.kt index 4ad37861..3e7f56e3 100644 --- a/domain/src/main/java/interactor/ReceiveSms.kt +++ b/domain/src/main/java/interactor/ReceiveSms.kt @@ -22,12 +22,14 @@ import android.telephony.SmsMessage import io.reactivex.Flowable import manager.ExternalBlockingManager import manager.NotificationManager +import repository.ConversationRepository import repository.MessageRepository import util.extensions.mapNotNull import java.util.concurrent.TimeUnit import javax.inject.Inject class ReceiveSms @Inject constructor( + private val conversationRepo: ConversationRepository, private val externalBlockingManager: ExternalBlockingManager, private val messageRepo: MessageRepository, private val notificationManager: NotificationManager, @@ -39,7 +41,8 @@ class ReceiveSms @Inject constructor( override fun buildObservable(params: Params): Flowable<*> { return Flowable.just(params) .filter { it.messages.isNotEmpty() } - .filter { // Don't continue if the sender is blocked + .filter { + // Don't continue if the sender is blocked val address = it.messages[0].displayOriginatingAddress !externalBlockingManager.shouldBlock(address).blockingGet() } @@ -53,10 +56,10 @@ class ReceiveSms @Inject constructor( messageRepo.insertReceivedSms(it.subId, address, body, time) // Add the message to the db } - .doOnNext { message -> messageRepo.updateConversations(message.threadId) } // Update the conversation - .mapNotNull { message -> messageRepo.getOrCreateConversation(message.threadId) } // Map message to conversation + .doOnNext { message -> conversationRepo.updateConversations(message.threadId) } // Update the conversation + .mapNotNull { message -> conversationRepo.getOrCreateConversation(message.threadId) } // Map message to conversation .filter { conversation -> !conversation.blocked } // Don't notify for blocked conversations - .doOnNext { conversation -> if (conversation.archived) messageRepo.markUnarchived(conversation.id) } // Unarchive conversation if necessary + .doOnNext { conversation -> if (conversation.archived) conversationRepo.markUnarchived(conversation.id) } // Unarchive conversation if necessary .map { conversation -> conversation.id } // Map to the id because [delay] will put us on the wrong thread .delay(1, TimeUnit.SECONDS) // Wait one second before trying to notify, in case the foreground app marks it as read first .doOnNext { threadId -> notificationManager.update(threadId) } // Update the notification diff --git a/domain/src/main/java/interactor/SendMessage.kt b/domain/src/main/java/interactor/SendMessage.kt index 7d7a193e..5a75c68d 100644 --- a/domain/src/main/java/interactor/SendMessage.kt +++ b/domain/src/main/java/interactor/SendMessage.kt @@ -20,10 +20,12 @@ package interactor import io.reactivex.Flowable import model.Attachment +import repository.ConversationRepository import repository.MessageRepository import javax.inject.Inject class SendMessage @Inject constructor( + private val conversationRepo: ConversationRepository, private val messageRepo: MessageRepository ) : Interactor() { @@ -40,9 +42,9 @@ class SendMessage @Inject constructor( } } // If this was the first message sent in the conversation, the conversation might not exist yet - .doOnNext { messageRepo.getOrCreateConversation(params.threadId) } - .doOnNext { messageRepo.updateConversations(params.threadId) } - .doOnNext { messageRepo.markUnarchived(params.threadId) } + .doOnNext { conversationRepo.getOrCreateConversation(params.threadId) } + .doOnNext { conversationRepo.updateConversations(params.threadId) } + .doOnNext { conversationRepo.markUnarchived(params.threadId) } } } \ No newline at end of file diff --git a/domain/src/main/java/interactor/SyncMessage.kt b/domain/src/main/java/interactor/SyncMessage.kt index 55f6924c..57804a1b 100644 --- a/domain/src/main/java/interactor/SyncMessage.kt +++ b/domain/src/main/java/interactor/SyncMessage.kt @@ -21,20 +21,20 @@ package interactor import android.net.Uri import io.reactivex.Flowable import model.Message -import repository.MessageRepository +import repository.ConversationRepository import repository.SyncRepository import util.extensions.mapNotNull import javax.inject.Inject class SyncMessage @Inject constructor( - private val messageRepo: MessageRepository, + private val conversationRepo: ConversationRepository, private val syncManager: SyncRepository ) : Interactor() { override fun buildObservable(params: Uri): Flowable { return Flowable.just(params) .mapNotNull { uri -> syncManager.syncMessage(uri) } - .doOnNext { message -> messageRepo.updateConversations(message.threadId) } + .doOnNext { message -> conversationRepo.updateConversations(message.threadId) } } } \ No newline at end of file diff --git a/domain/src/main/java/repository/ConversationRepository.kt b/domain/src/main/java/repository/ConversationRepository.kt new file mode 100644 index 00000000..a8e4bff1 --- /dev/null +++ b/domain/src/main/java/repository/ConversationRepository.kt @@ -0,0 +1,46 @@ +package repository + +import io.realm.RealmResults +import model.Conversation +import model.SearchResult + +interface ConversationRepository { + + fun getConversations(archived: Boolean = false): RealmResults + + fun getConversationsSnapshot(): List + + fun setConversationName(id: Long, name: String) + + fun searchConversations(query: String): List + + fun getBlockedConversations(): RealmResults + + fun getConversationAsync(threadId: Long): Conversation + + fun getConversation(threadId: Long): Conversation? + + fun getOrCreateConversation(threadId: Long): Conversation? + + fun getOrCreateConversation(address: String): Conversation? + + fun getOrCreateConversation(addresses: List): Conversation? + + fun saveDraft(threadId: Long, draft: String) + + /** + * Updates message-related fields in the conversation, like the date and snippet + */ + fun updateConversations(vararg threadIds: Long) + + fun markArchived(vararg threadIds: Long) + + fun markUnarchived(vararg threadIds: Long) + + fun markBlocked(vararg threadIds: Long) + + fun markUnblocked(vararg threadIds: Long) + + fun deleteConversations(vararg threadIds: Long) + +} \ No newline at end of file diff --git a/domain/src/main/java/repository/MessageRepository.kt b/domain/src/main/java/repository/MessageRepository.kt index ced0f283..7d8eed5f 100644 --- a/domain/src/main/java/repository/MessageRepository.kt +++ b/domain/src/main/java/repository/MessageRepository.kt @@ -20,35 +20,11 @@ package repository import io.realm.RealmResults import model.Attachment -import model.Conversation import model.Message import model.MmsPart -import model.SearchResult interface MessageRepository { - fun getConversations(archived: Boolean = false): RealmResults - - fun getConversationsSnapshot(): List - - fun setConversationName(id: Long, name: String) - - fun searchConversations(query: String): List - - fun getBlockedConversations(): RealmResults - - fun getConversationAsync(threadId: Long): Conversation - - fun getConversation(threadId: Long): Conversation? - - fun getOrCreateConversation(threadId: Long): Conversation? - - fun getOrCreateConversation(address: String): Conversation? - - fun getOrCreateConversation(addresses: List): Conversation? - - fun saveDraft(threadId: Long, draft: String) - fun getMessages(threadId: Long, query: String = ""): RealmResults fun getMessage(id: Long): Message? @@ -73,19 +49,6 @@ interface MessageRepository { */ fun getUnreadMessages(threadId: Long): RealmResults - /** - * Updates message-related fields in the conversation, like the date and snippet - */ - fun updateConversations(vararg threadIds: Long) - - fun markArchived(vararg threadIds: Long) - - fun markUnarchived(vararg threadIds: Long) - - fun markBlocked(vararg threadIds: Long) - - fun markUnblocked(vararg threadIds: Long) - fun markAllSeen() fun markSeen(threadId: Long) @@ -130,6 +93,4 @@ interface MessageRepository { fun deleteMessages(vararg messageIds: Long) - fun deleteConversations(vararg threadIds: Long) - } \ No newline at end of file diff --git a/presentation/src/main/java/common/util/NotificationManagerImpl.kt b/presentation/src/main/java/common/util/NotificationManagerImpl.kt index a7fba4f9..ddf17cd4 100644 --- a/presentation/src/main/java/common/util/NotificationManagerImpl.kt +++ b/presentation/src/main/java/common/util/NotificationManagerImpl.kt @@ -41,6 +41,7 @@ import mapper.CursorToPartImpl import receiver.MarkReadReceiver import receiver.MarkSeenReceiver import receiver.RemoteMessagingReceiver +import repository.ConversationRepository import repository.MessageRepository import util.Preferences import util.extensions.isImage @@ -51,8 +52,9 @@ import javax.inject.Singleton @Singleton class NotificationManagerImpl @Inject constructor( private val context: Context, - private val prefs: Preferences, private val colors: Colors, + private val conversationRepo: ConversationRepository, + private val prefs: Preferences, private val messageRepo: MessageRepository ) : manager.NotificationManager { @@ -96,7 +98,7 @@ class NotificationManagerImpl @Inject constructor( return } - val conversation = messageRepo.getConversation(threadId) ?: return + val conversation = conversationRepo.getConversation(threadId) ?: return val contentIntent = Intent(context, ComposeActivity::class.java).putExtra("threadId", threadId) val taskStackBuilder = TaskStackBuilder.create(context) @@ -221,7 +223,7 @@ class NotificationManagerImpl @Inject constructor( return } - val conversation = messageRepo.getConversation(message.threadId) ?: return + val conversation = conversationRepo.getConversation(message.threadId) ?: return val threadId = conversation.id val contentIntent = Intent(context, ComposeActivity::class.java).putExtra("threadId", threadId) @@ -270,7 +272,7 @@ class NotificationManagerImpl @Inject constructor( // Only proceed if the android version supports notification channels if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return - messageRepo.getConversation(threadId)?.let { conversation -> + conversationRepo.getConversation(threadId)?.let { conversation -> val channelId = buildNotificationChannelId(threadId) val name = conversation.getTitle() val importance = NotificationManager.IMPORTANCE_HIGH diff --git a/presentation/src/main/java/feature/blocked/BlockedViewModel.kt b/presentation/src/main/java/feature/blocked/BlockedViewModel.kt index 59749279..18a52098 100644 --- a/presentation/src/main/java/feature/blocked/BlockedViewModel.kt +++ b/presentation/src/main/java/feature/blocked/BlockedViewModel.kt @@ -26,21 +26,21 @@ import common.base.QkViewModel import interactor.MarkUnblocked import io.reactivex.rxkotlin.plusAssign import io.reactivex.rxkotlin.withLatestFrom -import repository.MessageRepository +import repository.ConversationRepository import util.Preferences import util.tryOrNull import javax.inject.Inject class BlockedViewModel @Inject constructor( private val context: Context, + private val conversationRepo: ConversationRepository, private val markUnblocked: MarkUnblocked, - private val messageRepo: MessageRepository, private val navigator: Navigator, private val prefs: Preferences ) : QkViewModel(BlockedState()) { init { - newState { copy(data = messageRepo.getBlockedConversations()) } + newState { copy(data = conversationRepo.getBlockedConversations()) } disposables += prefs.sia.asObservable() .subscribe { enabled -> newState { copy(siaEnabled = enabled) } } diff --git a/presentation/src/main/java/feature/compose/ComposeViewModel.kt b/presentation/src/main/java/feature/compose/ComposeViewModel.kt index edd96f37..e2339928 100644 --- a/presentation/src/main/java/feature/compose/ComposeViewModel.kt +++ b/presentation/src/main/java/feature/compose/ComposeViewModel.kt @@ -56,6 +56,7 @@ import model.Message import model.PhoneNumber import model.Recipient import repository.ContactRepository +import repository.ConversationRepository import repository.MessageRepository import util.ActiveSubscriptionObservable import util.extensions.asObservable @@ -78,6 +79,7 @@ class ComposeViewModel @Inject constructor( private val cancelMessage: CancelDelayedMessage, private val contactFilter: ContactFilter, private val contactsRepo: ContactRepository, + private val conversationRepo: ConversationRepository, private val deleteMessages: DeleteMessages, private val markRead: MarkRead, private val messageRepo: MessageRepository, @@ -104,7 +106,7 @@ class ComposeViewModel @Inject constructor( init { val initialConversation = threadId.takeIf { it != 0L } - ?.let(messageRepo::getConversationAsync) + ?.let(conversationRepo::getConversationAsync) ?.asObservable() ?: Observable.empty() @@ -120,7 +122,7 @@ class ComposeViewModel @Inject constructor( .doOnNext { newState { copy(loading = false) } } .switchMap { (threadId, addresses) -> val recipients = RealmList(*addresses.map { address -> Recipient(address = address) }.toTypedArray()) - messageRepo.getConversations().asObservable() + conversationRepo.getConversations().asObservable() .map { conversations -> conversations.filter { it.id == threadId } } .map { conversations -> conversations.firstOrNull() ?: Conversation(id = threadId, recipients = recipients) @@ -407,7 +409,7 @@ class ComposeViewModel @Inject constructor( .withLatestFrom(conversation, { _, conversation -> conversation.id }) .observeOn(Schedulers.io()) .withLatestFrom(view.textChangedIntent, { threadId, draft -> - messageRepo.saveDraft(threadId, draft.toString()) + conversationRepo.saveDraft(threadId, draft.toString()) }) .autoDisposable(view.scope()) .subscribe() @@ -533,7 +535,7 @@ class ComposeViewModel @Inject constructor( .map { number -> number.address } .forEach { addr -> val threadId = TelephonyCompat.getOrCreateThreadId(context, addr) - val address = listOf(messageRepo.getConversation(threadId)?.recipients?.firstOrNull()?.address + val address = listOf(conversationRepo.getConversation(threadId)?.recipients?.firstOrNull()?.address ?: addr) sendMessage.execute(SendMessage.Params(subId, threadId, address, body, attachments)) } diff --git a/presentation/src/main/java/feature/conversationinfo/ConversationInfoViewModel.kt b/presentation/src/main/java/feature/conversationinfo/ConversationInfoViewModel.kt index 1ec42794..6f02dd9a 100644 --- a/presentation/src/main/java/feature/conversationinfo/ConversationInfoViewModel.kt +++ b/presentation/src/main/java/feature/conversationinfo/ConversationInfoViewModel.kt @@ -32,6 +32,7 @@ import io.reactivex.rxkotlin.withLatestFrom import io.reactivex.subjects.BehaviorSubject import io.reactivex.subjects.Subject import model.Conversation +import repository.ConversationRepository import repository.MessageRepository import util.extensions.asObservable import javax.inject.Inject @@ -39,6 +40,7 @@ import javax.inject.Named class ConversationInfoViewModel @Inject constructor( @Named("threadId") threadId: Long, + private val conversationRepo: ConversationRepository, private val markArchived: MarkArchived, private val markUnarchived: MarkUnarchived, private val markBlocked: MarkBlocked, @@ -53,7 +55,7 @@ class ConversationInfoViewModel @Inject constructor( private val conversation: Subject = BehaviorSubject.create() init { - disposables += messageRepo.getConversationAsync(threadId) + disposables += conversationRepo.getConversationAsync(threadId) .asObservable() .filter { conversation -> conversation.isLoaded } .doOnNext { conversation -> @@ -109,7 +111,7 @@ class ConversationInfoViewModel @Inject constructor( // Set the conversation title view.nameChangedIntent .withLatestFrom(conversation) { name, conversation -> - messageRepo.setConversationName(conversation.id, name) + conversationRepo.setConversationName(conversation.id, name) } .autoDisposable(view.scope()) .subscribe() diff --git a/presentation/src/main/java/feature/gallery/GalleryViewModel.kt b/presentation/src/main/java/feature/gallery/GalleryViewModel.kt index 0db9d00e..d174efbc 100644 --- a/presentation/src/main/java/feature/gallery/GalleryViewModel.kt +++ b/presentation/src/main/java/feature/gallery/GalleryViewModel.kt @@ -29,6 +29,7 @@ import interactor.SaveImage import io.reactivex.Flowable import io.reactivex.rxkotlin.plusAssign import io.reactivex.rxkotlin.withLatestFrom +import repository.ConversationRepository import repository.MessageRepository import util.extensions.mapNotNull import javax.inject.Inject @@ -36,6 +37,7 @@ import javax.inject.Inject class GalleryViewModel @Inject constructor( private val intent: Intent, private val context: Context, + private val conversationRepo: ConversationRepository, private val messageRepo: MessageRepository, private val saveImage: SaveImage ) : QkViewModel(GalleryState()) { @@ -51,7 +53,7 @@ class GalleryViewModel @Inject constructor( disposables += partIdFlowable .mapNotNull { partId -> messageRepo.getMessageForPart(partId) } - .mapNotNull { message -> messageRepo.getConversation(message.threadId) } + .mapNotNull { message -> conversationRepo.getConversation(message.threadId) } .subscribe { conversation -> newState { copy(title = conversation.getTitle()) } } } diff --git a/presentation/src/main/java/feature/main/MainViewModel.kt b/presentation/src/main/java/feature/main/MainViewModel.kt index 0917de81..ada79b3b 100644 --- a/presentation/src/main/java/feature/main/MainViewModel.kt +++ b/presentation/src/main/java/feature/main/MainViewModel.kt @@ -42,6 +42,7 @@ import io.realm.Realm import manager.PermissionManager import manager.RatingManager import model.SyncLog +import repository.ConversationRepository import repository.MessageRepository import repository.SyncRepository import util.extensions.removeAccents @@ -51,6 +52,7 @@ import javax.inject.Inject class MainViewModel @Inject constructor( private val messageRepo: MessageRepository, private val markAllSeen: MarkAllSeen, + private val conversationRepo: ConversationRepository, private val deleteConversations: DeleteConversations, private val markRead: MarkRead, private val markUnread: MarkUnread, @@ -63,7 +65,7 @@ class MainViewModel @Inject constructor( private val ratingManager: RatingManager, private val syncMessages: SyncMessages, private val syncRepository: SyncRepository -) : QkViewModel(MainState(page = Inbox(data = messageRepo.getConversations()))) { +) : QkViewModel(MainState(page = Inbox(data = conversationRepo.getConversations()))) { init { disposables += deleteConversations @@ -138,7 +140,7 @@ class MainViewModel @Inject constructor( .map { query -> query.removeAccents() } .withLatestFrom(state, { query, state -> if (query.isEmpty() && state.page is Searching) { - newState { copy(page = Inbox(data = messageRepo.getConversations())) } + newState { copy(page = Inbox(data = conversationRepo.getConversations())) } } query }) @@ -150,7 +152,7 @@ class MainViewModel @Inject constructor( } } .observeOn(Schedulers.io()) - .switchMap { query -> Observable.just(query).map { messageRepo.searchConversations(it) } } + .switchMap { query -> Observable.just(query).map { conversationRepo.searchConversations(it) } } .autoDisposable(view.scope()) .subscribe { data -> newState { copy(page = Searching(loading = false, data = data)) } } @@ -186,8 +188,8 @@ class MainViewModel @Inject constructor( .distinctUntilChanged() .doOnNext { when (it) { - DrawerItem.INBOX -> newState { copy(page = Inbox(data = messageRepo.getConversations())) } - DrawerItem.ARCHIVED -> newState { copy(page = Archived(data = messageRepo.getConversations(true))) } + DrawerItem.INBOX -> newState { copy(page = Inbox(data = conversationRepo.getConversations())) } + DrawerItem.ARCHIVED -> newState { copy(page = Archived(data = conversationRepo.getConversations(true))) } DrawerItem.SCHEDULED -> newState { copy(page = Scheduled()) } else -> { } // Do nothing @@ -244,7 +246,7 @@ class MainViewModel @Inject constructor( view.conversationsSelectedIntent .withLatestFrom(state, { selection, state -> val read = selection - .mapNotNull(messageRepo::getConversation) + .mapNotNull(conversationRepo::getConversation) .sumBy { if (it.read) -1 else 1 } >= 0 val selected = selection.size @@ -321,7 +323,7 @@ class MainViewModel @Inject constructor( state.page is Archived && state.page.selected > 0 -> view.clearSelection() - state.page !is Inbox -> newState { copy(page = Inbox(data = messageRepo.getConversations())) } + state.page !is Inbox -> newState { copy(page = Inbox(data = conversationRepo.getConversations())) } else -> newState { copy(hasError = true) } } diff --git a/presentation/src/main/java/feature/notificationprefs/NotificationPrefsViewModel.kt b/presentation/src/main/java/feature/notificationprefs/NotificationPrefsViewModel.kt index 86809f6d..dbf30608 100644 --- a/presentation/src/main/java/feature/notificationprefs/NotificationPrefsViewModel.kt +++ b/presentation/src/main/java/feature/notificationprefs/NotificationPrefsViewModel.kt @@ -31,7 +31,7 @@ import common.base.QkViewModel import io.reactivex.Flowable import io.reactivex.rxkotlin.plusAssign import io.reactivex.schedulers.Schedulers -import repository.MessageRepository +import repository.ConversationRepository import util.Preferences import util.extensions.mapNotNull import javax.inject.Inject @@ -39,7 +39,7 @@ import javax.inject.Inject class NotificationPrefsViewModel @Inject constructor( private val intent: Intent, private val context: Context, - private val messageRepo: MessageRepository, + private val conversationRepo: ConversationRepository, private val navigator: Navigator, private val prefs: Preferences ) : QkViewModel(NotificationPrefsState()) { @@ -58,7 +58,7 @@ class NotificationPrefsViewModel @Inject constructor( ringtone = prefs.ringtone(threadId) disposables += Flowable.just(threadId) - .mapNotNull { threadId -> messageRepo.getConversation(threadId) } + .mapNotNull { threadId -> conversationRepo.getConversation(threadId) } .map { conversation -> conversation.getTitle() } .subscribeOn(Schedulers.io()) .subscribe { title -> newState { copy(conversationTitle = title) } } diff --git a/presentation/src/main/java/feature/qkreply/QkReplyViewModel.kt b/presentation/src/main/java/feature/qkreply/QkReplyViewModel.kt index 8b7748fb..405497c1 100644 --- a/presentation/src/main/java/feature/qkreply/QkReplyViewModel.kt +++ b/presentation/src/main/java/feature/qkreply/QkReplyViewModel.kt @@ -36,6 +36,7 @@ import io.reactivex.subjects.Subject import io.realm.RealmResults import model.Conversation import model.Message +import repository.ConversationRepository import repository.MessageRepository import util.ActiveSubscriptionObservable import util.extensions.asObservable @@ -46,6 +47,7 @@ import javax.inject.Named class QkReplyViewModel @Inject constructor( @Named("threadId") threadId: Long, + private val conversationRepo: ConversationRepository, private val markRead: MarkRead, private val messageRepo: MessageRepository, private val navigator: Navigator, @@ -54,7 +56,7 @@ class QkReplyViewModel @Inject constructor( ) : QkViewModel(QkReplyState()) { private val conversation by lazy { - messageRepo.getConversationAsync(threadId) + conversationRepo.getConversationAsync(threadId) .asObservable() .filter { it.isLoaded } .filter { it.isValid } @@ -183,7 +185,7 @@ class QkReplyViewModel @Inject constructor( .map { draft -> draft.toString() } .observeOn(Schedulers.io()) .withLatestFrom(conversation.map { it.id }, { draft, threadId -> - messageRepo.saveDraft(threadId, draft) + conversationRepo.saveDraft(threadId, draft) }) .autoDisposable(view.scope()) .subscribe() diff --git a/presentation/src/main/java/feature/widget/WidgetAdapter.kt b/presentation/src/main/java/feature/widget/WidgetAdapter.kt index ed0c3ffa..f8716147 100644 --- a/presentation/src/main/java/feature/widget/WidgetAdapter.kt +++ b/presentation/src/main/java/feature/widget/WidgetAdapter.kt @@ -41,7 +41,7 @@ import injection.appComponent import model.Contact import model.Conversation import model.PhoneNumber -import repository.MessageRepository +import repository.ConversationRepository import util.Preferences import javax.inject.Inject @@ -53,8 +53,8 @@ class WidgetAdapter(intent: Intent) : RemoteViewsService.RemoteViewsFactory { @Inject lateinit var context: Context @Inject lateinit var colors: Colors + @Inject lateinit var conversationRepo: ConversationRepository @Inject lateinit var dateFormatter: DateFormatter - @Inject lateinit var messageRepo: MessageRepository @Inject lateinit var prefs: Preferences private val appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID) @@ -83,7 +83,7 @@ class WidgetAdapter(intent: Intent) : RemoteViewsService.RemoteViewsFactory { } override fun onDataSetChanged() { - conversations = messageRepo.getConversationsSnapshot() + conversations = conversationRepo.getConversationsSnapshot() val remoteViews = RemoteViews(context.packageName, R.layout.widget) appWidgetManager.partiallyUpdateAppWidget(appWidgetId, remoteViews) diff --git a/presentation/src/main/java/injection/AppModule.kt b/presentation/src/main/java/injection/AppModule.kt index 1efb8016..48544474 100644 --- a/presentation/src/main/java/injection/AppModule.kt +++ b/presentation/src/main/java/injection/AppModule.kt @@ -53,6 +53,8 @@ import mapper.CursorToRecipientImpl import mapper.RatingManagerImpl import repository.ContactRepository import repository.ContactRepositoryImpl +import repository.ConversationRepository +import repository.ConversationRepositoryImpl import repository.ImageRepository import repository.ImageRepostoryImpl import repository.MessageRepository @@ -128,6 +130,9 @@ class AppModule(private var application: Application) { @Provides fun provideContactRepository(repository: ContactRepositoryImpl): ContactRepository = repository + @Provides + fun provideConversationRepository(repository: ConversationRepositoryImpl): ConversationRepository = repository + @Provides fun provideImageRepository(repository: ImageRepostoryImpl): ImageRepository = repository