forked from upstream-mirrors/quik
change compose sendintent (sending sms/mms) and sendmessage interactor to ensure convos congruency
change compose selectedconversation (chips -> convo monitoring) to take advantage of changes in conversationrepo conversationrepo change getthreadid to getconversation to avoid duplicate db access some cleanups and refactoring in conversationrepo
This commit is contained in:
@@ -19,46 +19,46 @@
|
||||
package dev.octoshrimpy.quik.compat
|
||||
|
||||
import android.content.Context
|
||||
import android.database.sqlite.SqliteWrapper
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.BaseColumns
|
||||
import android.provider.Telephony
|
||||
import android.text.TextUtils
|
||||
import android.util.Patterns
|
||||
import timber.log.Timber
|
||||
import java.util.regex.Pattern
|
||||
|
||||
object TelephonyCompat {
|
||||
class TelephonyCompat {
|
||||
companion object {
|
||||
val THREADS_CONTENT_URI = Telephony.Threads.CONTENT_URI
|
||||
|
||||
private val ID_PROJECTION = arrayOf(BaseColumns._ID)
|
||||
private val ID_PROJECTION = arrayOf(BaseColumns._ID)
|
||||
|
||||
private val THREAD_ID_CONTENT_URI = Uri.parse("content://mms-sms/threadID")
|
||||
private val THREAD_ID_CONTENT_URI = Uri.parse("content://mms-sms/threadID")
|
||||
|
||||
val NAME_ADDR_EMAIL_PATTERN = Pattern.compile("\\s*(\"[^\"]*\"|[^<>\"]+)\\s*<([^<>]+)>\\s*")
|
||||
val NAME_ADDR_EMAIL_PATTERN = Pattern.compile("\\s*(\"[^\"]*\"|[^<>\"]+)\\s*<([^<>]+)>\\s*")
|
||||
|
||||
fun getOrCreateThreadId(context: Context, recipient: String): Long {
|
||||
return getOrCreateThreadId(context, listOf(recipient))
|
||||
}
|
||||
|
||||
fun getOrCreateThreadId(context: Context, recipients: Collection<String>): Long {
|
||||
return Telephony.Threads.getOrCreateThreadId(context, recipients.toSet())
|
||||
}
|
||||
|
||||
fun extractAddrSpec(address: String): String {
|
||||
val match = NAME_ADDR_EMAIL_PATTERN.matcher(address)
|
||||
return if (match.matches()) {
|
||||
match.group(2)
|
||||
} else address
|
||||
}
|
||||
|
||||
fun isEmailAddress(address: String): Boolean {
|
||||
if (TextUtils.isEmpty(address)) {
|
||||
return false
|
||||
fun getOrCreateThreadId(context: Context, recipient: String): Long {
|
||||
return getOrCreateThreadId(context, listOf(recipient))
|
||||
}
|
||||
|
||||
fun getOrCreateThreadId(context: Context, recipients: Collection<String>): Long {
|
||||
return Telephony.Threads.getOrCreateThreadId(context, recipients.toSet())
|
||||
}
|
||||
|
||||
fun extractAddrSpec(address: String): String {
|
||||
val match = NAME_ADDR_EMAIL_PATTERN.matcher(address)
|
||||
return if (match.matches()) {
|
||||
match.group(2)
|
||||
} else address
|
||||
}
|
||||
|
||||
fun isEmailAddress(address: String): Boolean {
|
||||
if (TextUtils.isEmpty(address)) {
|
||||
return false
|
||||
}
|
||||
val s = extractAddrSpec(address)
|
||||
val match = Patterns.EMAIL_ADDRESS.matcher(s)
|
||||
return match.matches()
|
||||
}
|
||||
val s = extractAddrSpec(address)
|
||||
val match = Patterns.EMAIL_ADDRESS.matcher(s)
|
||||
return match.matches()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,8 +20,6 @@ package dev.octoshrimpy.quik.repository
|
||||
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.provider.Telephony
|
||||
import android.view.View.OnLongClickListener
|
||||
import dev.octoshrimpy.quik.compat.TelephonyCompat
|
||||
import dev.octoshrimpy.quik.extensions.anyOf
|
||||
import dev.octoshrimpy.quik.extensions.asObservable
|
||||
@@ -66,21 +64,21 @@ class ConversationRepositoryImpl @Inject constructor(
|
||||
}
|
||||
|
||||
return Realm.getDefaultInstance()
|
||||
.where(Conversation::class.java)
|
||||
.notEqualTo("id", 0L)
|
||||
.equalTo("archived", archived)
|
||||
.equalTo("blocked", false)
|
||||
.isNotEmpty("recipients")
|
||||
.beginGroup()
|
||||
.isNotNull("lastMessage")
|
||||
.or()
|
||||
.isNotEmpty("draft")
|
||||
.endGroup()
|
||||
.sort(
|
||||
sortOrder.toTypedArray(),
|
||||
sortDirections.toTypedArray()
|
||||
)
|
||||
.findAllAsync()
|
||||
.where(Conversation::class.java)
|
||||
.notEqualTo("id", 0L)
|
||||
.equalTo("archived", archived)
|
||||
.equalTo("blocked", false)
|
||||
.isNotEmpty("recipients")
|
||||
.beginGroup()
|
||||
.isNotNull("lastMessage")
|
||||
.or()
|
||||
.isNotEmpty("draft")
|
||||
.endGroup()
|
||||
.sort(
|
||||
sortOrder.toTypedArray(),
|
||||
sortDirections.toTypedArray()
|
||||
)
|
||||
.findAllAsync()
|
||||
}
|
||||
|
||||
override fun getConversationsSnapshot(unreadAtTop: Boolean): List<Conversation> {
|
||||
@@ -94,60 +92,49 @@ class ConversationRepositoryImpl @Inject constructor(
|
||||
|
||||
return Realm.getDefaultInstance().use { realm ->
|
||||
realm.refresh()
|
||||
realm.copyFromRealm(realm.where(Conversation::class.java)
|
||||
.notEqualTo("id", 0L)
|
||||
.equalTo("archived", false)
|
||||
.equalTo("blocked", false)
|
||||
.isNotEmpty("recipients")
|
||||
.beginGroup()
|
||||
.isNotNull("lastMessage")
|
||||
.or()
|
||||
.isNotEmpty("draft")
|
||||
.endGroup()
|
||||
.sort(
|
||||
sortOrder.toTypedArray(),
|
||||
sortDirections.toTypedArray()
|
||||
)
|
||||
.findAll())
|
||||
realm.where(Conversation::class.java)
|
||||
.notEqualTo("id", 0L)
|
||||
.equalTo("archived", false)
|
||||
.equalTo("blocked", false)
|
||||
.isNotEmpty("recipients")
|
||||
.beginGroup()
|
||||
.isNotNull("lastMessage")
|
||||
.or()
|
||||
.isNotEmpty("draft")
|
||||
.endGroup()
|
||||
.sort(sortOrder.toTypedArray(), sortDirections.toTypedArray())
|
||||
.findAll()
|
||||
.let(realm::copyFromRealm)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getTopConversations(): List<Conversation> {
|
||||
return Realm.getDefaultInstance().use { realm ->
|
||||
realm.copyFromRealm(realm.where(Conversation::class.java)
|
||||
.notEqualTo("id", 0L)
|
||||
.isNotNull("lastMessage")
|
||||
.beginGroup()
|
||||
.equalTo("pinned", true)
|
||||
.or()
|
||||
.greaterThan("lastMessage.date", System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7))
|
||||
.endGroup()
|
||||
.equalTo("archived", false)
|
||||
.equalTo("blocked", false)
|
||||
.isNotEmpty("recipients")
|
||||
.findAll())
|
||||
.sortedWith(compareByDescending<Conversation> { conversation -> conversation.pinned }
|
||||
.thenByDescending { conversation ->
|
||||
realm.where(Message::class.java)
|
||||
.equalTo("threadId", conversation.id)
|
||||
.greaterThan("date", System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7))
|
||||
.count()
|
||||
})
|
||||
override fun getTopConversations() =
|
||||
Realm.getDefaultInstance().use { realm ->
|
||||
realm.where(Conversation::class.java)
|
||||
.notEqualTo("id", 0L)
|
||||
.isNotNull("lastMessage")
|
||||
.beginGroup()
|
||||
.equalTo("pinned", true)
|
||||
.or()
|
||||
.greaterThan("lastMessage.date", System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7))
|
||||
.endGroup()
|
||||
.equalTo("archived", false)
|
||||
.equalTo("blocked", false)
|
||||
.isNotEmpty("recipients")
|
||||
.findAll()
|
||||
.let(realm::copyFromRealm)
|
||||
.sortedWith(compareByDescending<Conversation> { conversation -> conversation.pinned }
|
||||
.thenByDescending { conversation ->
|
||||
realm.where(Message::class.java)
|
||||
.equalTo("threadId", conversation.id)
|
||||
.greaterThan("date", System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7))
|
||||
.count()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 setConversationName(id: Long, name: String): Completable {
|
||||
return Completable.fromAction {
|
||||
override fun setConversationName(id: Long, name: String) =
|
||||
Completable.fromAction {
|
||||
Realm.getDefaultInstance().use { realm ->
|
||||
realm.executeTransaction {
|
||||
realm.where(Conversation::class.java)
|
||||
@@ -157,150 +144,134 @@ class ConversationRepositoryImpl @Inject constructor(
|
||||
}
|
||||
}
|
||||
}.subscribeOn(Schedulers.io()) // Ensure the operation is performed on a background thread
|
||||
}
|
||||
|
||||
override fun searchConversations(query: CharSequence): List<SearchResult> {
|
||||
val realm = Realm.getDefaultInstance()
|
||||
|
||||
val normalizedQuery = query.removeAccents()
|
||||
val conversations = realm.copyFromRealm(realm
|
||||
.where(Conversation::class.java)
|
||||
.notEqualTo("id", 0L)
|
||||
.isNotNull("lastMessage")
|
||||
.equalTo("blocked", false)
|
||||
.isNotEmpty("recipients")
|
||||
.sort("pinned", Sort.DESCENDING, "lastMessage.date", Sort.DESCENDING)
|
||||
.findAll())
|
||||
.where(Conversation::class.java)
|
||||
.notEqualTo("id", 0L)
|
||||
.isNotNull("lastMessage")
|
||||
.equalTo("blocked", false)
|
||||
.isNotEmpty("recipients")
|
||||
.sort("pinned", Sort.DESCENDING, "lastMessage.date", Sort.DESCENDING)
|
||||
.findAll())
|
||||
|
||||
val messagesByConversation = realm.copyFromRealm(realm
|
||||
.where(Message::class.java)
|
||||
.beginGroup()
|
||||
.contains("body", normalizedQuery, Case.INSENSITIVE)
|
||||
.or()
|
||||
.contains("parts.text", normalizedQuery, Case.INSENSITIVE)
|
||||
.endGroup()
|
||||
.findAll())
|
||||
.asSequence()
|
||||
.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(normalizedQuery, conversation, messages) }
|
||||
.sortedByDescending { result -> result.messages }
|
||||
.toList()
|
||||
.where(Message::class.java)
|
||||
.beginGroup()
|
||||
.contains("body", normalizedQuery, Case.INSENSITIVE)
|
||||
.or()
|
||||
.contains("parts.text", normalizedQuery, Case.INSENSITIVE)
|
||||
.endGroup()
|
||||
.findAll())
|
||||
.asSequence()
|
||||
.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(normalizedQuery, conversation, messages) }
|
||||
.sortedByDescending { result -> result.messages }
|
||||
.toList()
|
||||
|
||||
realm.close()
|
||||
|
||||
return conversations
|
||||
.filter { conversation -> conversationFilter.filter(conversation, normalizedQuery) }
|
||||
.map { conversation -> SearchResult(normalizedQuery, conversation, 0) } + messagesByConversation
|
||||
.filter { conversation -> conversationFilter.filter(conversation, normalizedQuery) }
|
||||
.map {
|
||||
conversation -> SearchResult(normalizedQuery, conversation, 0)
|
||||
} + messagesByConversation
|
||||
}
|
||||
|
||||
override fun getBlockedConversations(): RealmResults<Conversation> {
|
||||
return Realm.getDefaultInstance()
|
||||
.where(Conversation::class.java)
|
||||
.equalTo("blocked", true)
|
||||
.sort(
|
||||
arrayOf("lastMessage.date"),
|
||||
arrayOf(Sort.DESCENDING)
|
||||
)
|
||||
.findAll()
|
||||
}
|
||||
override fun getBlockedConversations(): RealmResults<Conversation> =
|
||||
Realm.getDefaultInstance()
|
||||
.where(Conversation::class.java)
|
||||
.equalTo("blocked", true)
|
||||
.sort(
|
||||
arrayOf("lastMessage.date"),
|
||||
arrayOf(Sort.DESCENDING)
|
||||
)
|
||||
.findAll()
|
||||
|
||||
override fun getBlockedConversationsAsync(): RealmResults<Conversation> {
|
||||
return Realm.getDefaultInstance()
|
||||
override fun getBlockedConversationsAsync(): RealmResults<Conversation> =
|
||||
Realm.getDefaultInstance()
|
||||
.where(Conversation::class.java)
|
||||
.equalTo("blocked", true)
|
||||
.sort(
|
||||
arrayOf("lastMessage.date"),
|
||||
arrayOf(Sort.DESCENDING)
|
||||
)
|
||||
.findAllAsync()
|
||||
|
||||
override fun getConversationAsync(threadId: Long): Conversation =
|
||||
Realm.getDefaultInstance()
|
||||
.where(Conversation::class.java)
|
||||
.equalTo("id", threadId)
|
||||
.findFirstAsync()
|
||||
|
||||
override fun getConversation(threadId: Long) =
|
||||
Realm.getDefaultInstance()
|
||||
.apply { refresh() }
|
||||
.where(Conversation::class.java)
|
||||
.equalTo("id", threadId)
|
||||
.findFirst()
|
||||
|
||||
override fun getUnseenIds(archived: Boolean) =
|
||||
ArrayList<Long>().apply {
|
||||
Realm.getDefaultInstance()
|
||||
.where(Conversation::class.java)
|
||||
.equalTo("blocked", true)
|
||||
.notEqualTo("id", 0L)
|
||||
.equalTo("archived", archived)
|
||||
.equalTo("blocked", false)
|
||||
.equalTo("lastMessage.seen", false)
|
||||
.sort(
|
||||
arrayOf("lastMessage.date"),
|
||||
arrayOf(Sort.DESCENDING)
|
||||
)
|
||||
.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()
|
||||
.apply { refresh() }
|
||||
.where(Conversation::class.java)
|
||||
.equalTo("id", threadId)
|
||||
.findFirst()
|
||||
}
|
||||
|
||||
override fun getUnseenIds(archived: Boolean): List<Long> {
|
||||
val conversationIds = ArrayList<Long>()
|
||||
|
||||
Realm.getDefaultInstance()
|
||||
.where(Conversation::class.java)
|
||||
.notEqualTo("id", 0L)
|
||||
.equalTo("archived", archived)
|
||||
.equalTo("blocked", false)
|
||||
.equalTo("lastMessage.seen", false)
|
||||
.sort(
|
||||
arrayOf("lastMessage.date"),
|
||||
arrayOf(Sort.DESCENDING)
|
||||
)
|
||||
.findAllAsync()
|
||||
.forEach { conversation -> conversationIds.add(conversation.id) }
|
||||
|
||||
return conversationIds
|
||||
}
|
||||
|
||||
override fun getUnreadIds(archived: Boolean): List<Long> {
|
||||
val conversationIds = ArrayList<Long>()
|
||||
|
||||
Realm.getDefaultInstance()
|
||||
.where(Conversation::class.java)
|
||||
.notEqualTo("id", 0L)
|
||||
.equalTo("archived", archived)
|
||||
.equalTo("blocked", false)
|
||||
.equalTo("lastMessage.read", false)
|
||||
.sort(
|
||||
arrayOf("lastMessage.date"),
|
||||
arrayOf(Sort.DESCENDING)
|
||||
)
|
||||
.findAllAsync()
|
||||
.forEach { conversation -> conversationIds.add(conversation.id) }
|
||||
|
||||
return conversationIds
|
||||
}
|
||||
|
||||
override fun getConversationAndLastSenderContactName(threadId: Long): Pair<Conversation?, String?>? {
|
||||
val conversation = Realm.getDefaultInstance()
|
||||
.apply { refresh() }
|
||||
.where(Conversation::class.java)
|
||||
.equalTo("id", threadId)
|
||||
.findFirst()
|
||||
|
||||
if (conversation === null)
|
||||
return null
|
||||
|
||||
var conversationLastSmsSender: String? = null
|
||||
|
||||
if (conversation !== null) {
|
||||
conversationLastSmsSender = conversation?.recipients?.find { recipient ->
|
||||
phoneNumberUtils.compare(recipient.address, conversation.lastMessage!!.address)
|
||||
}?.contact?.name
|
||||
.forEach { conversation -> add(conversation.id) }
|
||||
}
|
||||
|
||||
return return Pair(conversation, conversationLastSmsSender)
|
||||
}
|
||||
|
||||
override fun getConversations(vararg threadIds: Long): RealmResults<Conversation> {
|
||||
return Realm.getDefaultInstance()
|
||||
override fun getUnreadIds(archived: Boolean) =
|
||||
ArrayList<Long>().apply {
|
||||
Realm.getDefaultInstance()
|
||||
.where(Conversation::class.java)
|
||||
.anyOf("id", threadIds)
|
||||
.findAll()
|
||||
}
|
||||
.notEqualTo("id", 0L)
|
||||
.equalTo("archived", archived)
|
||||
.equalTo("blocked", false)
|
||||
.equalTo("lastMessage.read", false)
|
||||
.sort(
|
||||
arrayOf("lastMessage.date"),
|
||||
arrayOf(Sort.DESCENDING)
|
||||
)
|
||||
.findAllAsync()
|
||||
.forEach { conversation -> add(conversation.id) }
|
||||
}
|
||||
|
||||
override fun getUnmanagedConversations(): Observable<List<Conversation>> {
|
||||
val realm = Realm.getDefaultInstance()
|
||||
return realm.where(Conversation::class.java)
|
||||
override fun getConversationAndLastSenderContactName(threadId: Long): Pair<Conversation?, String?>? =
|
||||
Realm.getDefaultInstance()
|
||||
.apply { refresh() }
|
||||
.where(Conversation::class.java)
|
||||
.equalTo("id", threadId)
|
||||
.findFirst()
|
||||
?.let { conversation ->
|
||||
val conversationLastSmsSender: String? = conversation.recipients.find { recipient ->
|
||||
phoneNumberUtils.compare(recipient.address, conversation.lastMessage!!.address)
|
||||
}?.contact?.name
|
||||
|
||||
Pair(conversation, conversationLastSmsSender)
|
||||
}
|
||||
|
||||
override fun getConversations(vararg threadIds: Long): RealmResults<Conversation> =
|
||||
Realm.getDefaultInstance()
|
||||
.where(Conversation::class.java)
|
||||
.anyOf("id", threadIds)
|
||||
.findAll()
|
||||
|
||||
override fun getUnmanagedConversations(): Observable<List<Conversation>> =
|
||||
Realm.getDefaultInstance().let { realm->
|
||||
realm.where(Conversation::class.java)
|
||||
.sort("lastMessage.date", Sort.DESCENDING)
|
||||
.notEqualTo("id", 0L)
|
||||
.isNotNull("lastMessage")
|
||||
@@ -315,164 +286,138 @@ class ConversationRepositoryImpl @Inject constructor(
|
||||
.map { realm.copyFromRealm(it) }
|
||||
.subscribeOn(AndroidSchedulers.mainThread())
|
||||
.observeOn(Schedulers.io())
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRecipients(): RealmResults<Recipient> {
|
||||
val realm = Realm.getDefaultInstance()
|
||||
return realm.where(Recipient::class.java)
|
||||
override fun getRecipients(): RealmResults<Recipient> =
|
||||
Realm.getDefaultInstance()
|
||||
.where(Recipient::class.java)
|
||||
.findAll()
|
||||
}
|
||||
|
||||
override fun getUnmanagedRecipients(): Observable<List<Recipient>> {
|
||||
val realm = Realm.getDefaultInstance()
|
||||
return realm.where(Recipient::class.java)
|
||||
override fun getUnmanagedRecipients(): Observable<List<Recipient>> =
|
||||
Realm.getDefaultInstance().let { realm ->
|
||||
realm.where(Recipient::class.java)
|
||||
.isNotNull("contact")
|
||||
.findAllAsync()
|
||||
.asObservable()
|
||||
.filter { it.isLoaded && it.isValid }
|
||||
.map { realm.copyFromRealm(it) }
|
||||
.subscribeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRecipient(recipientId: Long): Recipient? {
|
||||
return Realm.getDefaultInstance()
|
||||
.where(Recipient::class.java)
|
||||
.equalTo("id", recipientId)
|
||||
.findFirst()
|
||||
}
|
||||
override fun getRecipient(recipientId: Long): Recipient? =
|
||||
Realm.getDefaultInstance()
|
||||
.where(Recipient::class.java)
|
||||
.equalTo("id", recipientId)
|
||||
.findFirst()
|
||||
|
||||
override fun getThreadId(recipient: String): Long? {
|
||||
return getThreadId(listOf(recipient))
|
||||
}
|
||||
override fun getConversation(recipient: String) =
|
||||
getConversation(listOf(recipient))
|
||||
|
||||
override fun getThreadId(recipients: Collection<String>): Long? {
|
||||
return Realm.getDefaultInstance().use { realm ->
|
||||
override fun getConversation(recipients: Collection<String>) =
|
||||
Realm.getDefaultInstance().use { realm ->
|
||||
realm.refresh()
|
||||
realm.where(Conversation::class.java)
|
||||
.findAll()
|
||||
.asSequence()
|
||||
.filter { conversation -> conversation.recipients.size == recipients.size }
|
||||
.find { conversation ->
|
||||
conversation.recipients.map { it.address }.all { address ->
|
||||
recipients.any { recipient -> phoneNumberUtils.compare(recipient, address) }
|
||||
}
|
||||
}?.id
|
||||
}
|
||||
}
|
||||
|
||||
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<String>): Conversation? {
|
||||
if (addresses.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (getThreadId(addresses) ?: tryOrNull { TelephonyCompat.getOrCreateThreadId(context, addresses.toSet()) })
|
||||
?.takeIf { threadId -> threadId != 0L }
|
||||
?.let { threadId ->
|
||||
getConversation(threadId)
|
||||
?.let(Realm.getDefaultInstance()::copyFromRealm)
|
||||
?: getConversationFromCp(threadId)
|
||||
.findAll()
|
||||
.asSequence()
|
||||
.filter { conversation -> conversation.recipients.size == recipients.size }
|
||||
.find { conversation ->
|
||||
conversation.recipients.map { it.address }.all { address ->
|
||||
recipients.any { recipient -> phoneNumberUtils.compare(recipient, address) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun saveDraft(threadId: Long, draft: String) {
|
||||
override fun getOrCreateConversation(threadId: Long) =
|
||||
tryOrNull(true) {
|
||||
getConversation(threadId) ?: createConversationFromCp(threadId)
|
||||
}
|
||||
|
||||
override fun getOrCreateConversation(address: String) =
|
||||
getOrCreateConversation(listOf(address))
|
||||
|
||||
override fun getOrCreateConversation(addresses: Collection<String>) =
|
||||
tryOrNull(true) {
|
||||
getConversation(addresses)
|
||||
?: tryOrNull { TelephonyCompat.getOrCreateThreadId(context, addresses.toSet()) }
|
||||
?.takeIf { it != 0L }
|
||||
?.let { threadId -> getOrCreateConversation(threadId) }
|
||||
}
|
||||
|
||||
override fun saveDraft(threadId: Long, draft: String) =
|
||||
Realm.getDefaultInstance().use { realm ->
|
||||
realm.refresh()
|
||||
|
||||
val conversation = realm.where(Conversation::class.java)
|
||||
.equalTo("id", threadId)
|
||||
.findFirst()
|
||||
.equalTo("id", threadId)
|
||||
.findFirst()
|
||||
|
||||
realm.executeTransaction {
|
||||
conversation?.takeIf { it.isValid }?.draft = draft
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateConversations(vararg threadIds: Long) {
|
||||
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
|
||||
.where(Conversation::class.java)
|
||||
.equalTo("id", threadId)
|
||||
.findFirst() ?: return@forEach
|
||||
|
||||
val message = realm
|
||||
.where(Message::class.java)
|
||||
.equalTo("threadId", threadId)
|
||||
.sort("date", Sort.DESCENDING)
|
||||
.findFirst()
|
||||
.where(Message::class.java)
|
||||
.equalTo("threadId", threadId)
|
||||
.sort("date", Sort.DESCENDING)
|
||||
.findFirst()
|
||||
|
||||
realm.executeTransaction {
|
||||
conversation.lastMessage = message
|
||||
}
|
||||
realm.executeTransaction { conversation.lastMessage = message }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun markArchived(vararg threadIds: Long) {
|
||||
override fun markArchived(vararg threadIds: Long) =
|
||||
Realm.getDefaultInstance().use { realm ->
|
||||
val conversations = realm.where(Conversation::class.java)
|
||||
.anyOf("id", threadIds)
|
||||
.findAll()
|
||||
.anyOf("id", threadIds)
|
||||
.findAll()
|
||||
|
||||
realm.executeTransaction {
|
||||
conversations.forEach { it.archived = true }
|
||||
}
|
||||
realm.executeTransaction { conversations.forEach { it.archived = true } }
|
||||
}
|
||||
}
|
||||
|
||||
override fun markUnarchived(vararg threadIds: Long) {
|
||||
override fun markUnarchived(vararg threadIds: Long) =
|
||||
Realm.getDefaultInstance().use { realm ->
|
||||
val conversations = realm.where(Conversation::class.java)
|
||||
.anyOf("id", threadIds)
|
||||
.findAll()
|
||||
.anyOf("id", threadIds)
|
||||
.findAll()
|
||||
|
||||
realm.executeTransaction {
|
||||
conversations.forEach { it.archived = false }
|
||||
}
|
||||
realm.executeTransaction { conversations.forEach { it.archived = false } }
|
||||
}
|
||||
}
|
||||
|
||||
override fun markPinned(vararg threadIds: Long) {
|
||||
override fun markPinned(vararg threadIds: Long) =
|
||||
Realm.getDefaultInstance().use { realm ->
|
||||
val conversations = realm.where(Conversation::class.java)
|
||||
.anyOf("id", threadIds)
|
||||
.findAll()
|
||||
.anyOf("id", threadIds)
|
||||
.findAll()
|
||||
|
||||
realm.executeTransaction {
|
||||
conversations.forEach { it.pinned = true }
|
||||
}
|
||||
realm.executeTransaction { conversations.forEach { it.pinned = true } }
|
||||
}
|
||||
}
|
||||
|
||||
override fun markUnpinned(vararg threadIds: Long) {
|
||||
override fun markUnpinned(vararg threadIds: Long) =
|
||||
Realm.getDefaultInstance().use { realm ->
|
||||
val conversations = realm.where(Conversation::class.java)
|
||||
.anyOf("id", threadIds)
|
||||
.findAll()
|
||||
.anyOf("id", threadIds)
|
||||
.findAll()
|
||||
|
||||
realm.executeTransaction {
|
||||
conversations.forEach { it.pinned = false }
|
||||
}
|
||||
realm.executeTransaction { conversations.forEach { it.pinned = false } }
|
||||
}
|
||||
}
|
||||
|
||||
override fun markBlocked(threadIds: List<Long>, blockingClient: Int, blockReason: String?) {
|
||||
override fun markBlocked(threadIds: Collection<Long>, blockingClient: Int, blockReason: String?) =
|
||||
Realm.getDefaultInstance().use { realm ->
|
||||
val conversations = realm.where(Conversation::class.java)
|
||||
.anyOf("id", threadIds.toLongArray())
|
||||
.equalTo("blocked", false)
|
||||
.findAll()
|
||||
.anyOf("id", threadIds.toLongArray())
|
||||
.equalTo("blocked", false)
|
||||
.findAll()
|
||||
|
||||
realm.executeTransaction {
|
||||
conversations.forEach { conversation ->
|
||||
@@ -482,13 +427,12 @@ class ConversationRepositoryImpl @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun markUnblocked(vararg threadIds: Long) {
|
||||
override fun markUnblocked(vararg threadIds: Long) =
|
||||
Realm.getDefaultInstance().use { realm ->
|
||||
val conversations = realm.where(Conversation::class.java)
|
||||
.anyOf("id", threadIds)
|
||||
.findAll()
|
||||
.anyOf("id", threadIds)
|
||||
.findAll()
|
||||
|
||||
realm.executeTransaction {
|
||||
conversations.forEach { conversation ->
|
||||
@@ -498,12 +442,15 @@ class ConversationRepositoryImpl @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()
|
||||
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()
|
||||
@@ -511,9 +458,12 @@ class ConversationRepositoryImpl @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
threadIds.forEach { threadId ->
|
||||
val uri = ContentUris.withAppendedId(Telephony.Threads.CONTENT_URI, threadId)
|
||||
context.contentResolver.delete(uri, null, null)
|
||||
threadIds.forEach {
|
||||
context.contentResolver.delete(
|
||||
ContentUris.withAppendedId(TelephonyCompat.THREADS_CONTENT_URI, it),
|
||||
null,
|
||||
null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -524,40 +474,45 @@ class ConversationRepositoryImpl @Inject constructor(
|
||||
* 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.getConversationsCursor()
|
||||
private fun createConversationFromCp(threadId: Long) =
|
||||
tryOrNull(true) {
|
||||
cursorToConversation.getConversationsCursor()
|
||||
?.map(cursorToConversation::map)
|
||||
?.firstOrNull { it.id == threadId }
|
||||
?.let { conversation ->
|
||||
val realm = Realm.getDefaultInstance()
|
||||
val contacts = realm.copyFromRealm(realm.where(Contact::class.java).findAll())
|
||||
val lastMessage = realm.where(Message::class.java).equalTo("threadId", threadId)
|
||||
.sort("date", Sort.DESCENDING).findFirst()?.let(realm::copyFromRealm)
|
||||
?.firstOrNull { conversation -> conversation.id == threadId }
|
||||
?.also { conversation ->
|
||||
Realm.getDefaultInstance().use { realm ->
|
||||
val realmContacts = 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) } }
|
||||
// match recipients from provider to recipients in realm
|
||||
val matchedRecipients = conversation.recipients
|
||||
.mapNotNull { recipient ->
|
||||
// map the recipient cursor to a list of recipients
|
||||
cursorToRecipient.getRecipientCursor(recipient.id)?.use { cursor ->
|
||||
cursor.map { cursorToRecipient.map(it) }
|
||||
}
|
||||
}
|
||||
.flatten()
|
||||
.map { recipient ->
|
||||
recipient.apply {
|
||||
contact = contacts.firstOrNull {
|
||||
it.numbers.any { phoneNumberUtils.compare(recipient.address, it.address) }
|
||||
contact = realmContacts.firstOrNull { realmContact ->
|
||||
realmContact.numbers.any {
|
||||
phoneNumberUtils.compare(it.address, address)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conversation.recipients.clear()
|
||||
conversation.recipients.addAll(recipients)
|
||||
conversation.lastMessage = lastMessage
|
||||
realm.executeTransaction { it.insertOrUpdate(conversation) }
|
||||
realm.close()
|
||||
conversation.apply {
|
||||
recipients.clear()
|
||||
recipients.addAll(matchedRecipients)
|
||||
lastMessage = realm.where(Message::class.java)
|
||||
.equalTo("threadId", threadId)
|
||||
.sort("date", Sort.DESCENDING)
|
||||
.findFirst()
|
||||
}
|
||||
|
||||
conversation
|
||||
realm.executeTransaction { it.insertOrUpdate(conversation) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,6 @@
|
||||
package dev.octoshrimpy.quik.interactor
|
||||
|
||||
import android.content.Context
|
||||
import dev.octoshrimpy.quik.compat.TelephonyCompat
|
||||
import dev.octoshrimpy.quik.extensions.mapNotNull
|
||||
import dev.octoshrimpy.quik.model.Attachment
|
||||
import dev.octoshrimpy.quik.repository.ConversationRepository
|
||||
import dev.octoshrimpy.quik.repository.MessageRepository
|
||||
@@ -44,29 +42,29 @@ class SendMessage @Inject constructor(
|
||||
)
|
||||
|
||||
override fun buildObservable(params: Params): Flowable<*> = Flowable.just(Unit)
|
||||
.filter { params.addresses.isNotEmpty() }
|
||||
.doOnNext {
|
||||
// If a threadId isn't provided, try to obtain one
|
||||
val threadId = when (params.threadId) {
|
||||
0L -> TelephonyCompat.getOrCreateThreadId(context, params.addresses.toSet())
|
||||
else -> params.threadId
|
||||
}
|
||||
messageRepo.sendMessage(params.subId, threadId, params.addresses, params.body, params.attachments,
|
||||
params.delay)
|
||||
.filter { params.addresses.isNotEmpty() }
|
||||
.doOnNext {
|
||||
// If a threadId isn't provided, try to obtain one
|
||||
val threadId = when (params.threadId) {
|
||||
0L -> conversationRepo.getOrCreateConversation(params.addresses)?.id ?: 0
|
||||
else -> params.threadId
|
||||
}
|
||||
|
||||
// delete attachment local files, if any, because they're saved to mms db by now
|
||||
params.attachments.forEach { it.removeCacheFile() }
|
||||
// if unable to find or create a conversation (and/or underlying threadId), fail
|
||||
if (threadId == 0L)
|
||||
return@doOnNext
|
||||
|
||||
params.apply {
|
||||
messageRepo.sendMessage(subId, threadId, addresses, body, attachments, delay)
|
||||
}
|
||||
.mapNotNull {
|
||||
// If the threadId wasn't provided, then it's probably because it doesn't exist in Realm.
|
||||
// Sync it now and get the id
|
||||
when (params.threadId) {
|
||||
0L -> conversationRepo.getOrCreateConversation(params.addresses)?.id
|
||||
else -> params.threadId
|
||||
}
|
||||
}
|
||||
.doOnNext { threadId -> conversationRepo.updateConversations(threadId) }
|
||||
.doOnNext { threadId -> conversationRepo.markUnarchived(threadId) }
|
||||
.flatMap { updateBadge.buildObservable(Unit) } // Update the widget
|
||||
|
||||
conversationRepo.updateConversations(threadId)
|
||||
|
||||
conversationRepo.markUnarchived(threadId)
|
||||
|
||||
// delete attachment local files, if any, because they're saved to mms db by now
|
||||
params.attachments.forEach { it.removeCacheFile() }
|
||||
}
|
||||
.flatMap { updateBadge.buildObservable(Unit) } // Update the widget
|
||||
|
||||
}
|
||||
@@ -67,15 +67,15 @@ interface ConversationRepository {
|
||||
|
||||
fun getRecipient(recipientId: Long): Recipient?
|
||||
|
||||
fun getThreadId(recipient: String): Long?
|
||||
fun getConversation(recipient: String): Conversation?
|
||||
|
||||
fun getThreadId(recipients: Collection<String>): Long?
|
||||
fun getConversation(recipients: Collection<String>): Conversation?
|
||||
|
||||
fun getOrCreateConversation(threadId: Long): Conversation?
|
||||
|
||||
fun getOrCreateConversation(address: String): Conversation?
|
||||
|
||||
fun getOrCreateConversation(addresses: List<String>): Conversation?
|
||||
fun getOrCreateConversation(addresses: Collection<String>): Conversation?
|
||||
|
||||
fun saveDraft(threadId: Long, draft: String)
|
||||
|
||||
@@ -92,7 +92,7 @@ interface ConversationRepository {
|
||||
|
||||
fun markUnpinned(vararg threadIds: Long)
|
||||
|
||||
fun markBlocked(threadIds: List<Long>, blockingClient: Int, blockReason: String?)
|
||||
fun markBlocked(threadIds: Collection<Long>, blockingClient: Int, blockReason: String?)
|
||||
|
||||
fun markUnblocked(vararg threadIds: Long)
|
||||
|
||||
|
||||
+2
-2
@@ -42,8 +42,8 @@ class BlockedNumbersPresenter @Inject constructor(
|
||||
.observeOn(Schedulers.io())
|
||||
.doOnNext { id ->
|
||||
blockingRepo.getBlockedNumber(id)?.address
|
||||
?.let(conversationRepo::getThreadId)
|
||||
?.let { threadId -> markUnblocked.execute(listOf(threadId)) }
|
||||
?.let(conversationRepo::getConversation)
|
||||
?.let { conversation -> markUnblocked.execute(listOf(conversation.id)) }
|
||||
}
|
||||
.doOnNext(blockingRepo::unblockNumber)
|
||||
.subscribeOn(Schedulers.io())
|
||||
|
||||
@@ -156,56 +156,39 @@ class ComposeViewModel @Inject constructor(
|
||||
newState { copy(attachments = sharedAttachments) }
|
||||
|
||||
val initialConversation = threadId.takeIf { it != 0L }
|
||||
?.let(conversationRepo::getConversationAsync)
|
||||
?.asObservable()
|
||||
?: Observable.empty()
|
||||
?.let(conversationRepo::getConversationAsync)
|
||||
?.asObservable()
|
||||
?: Observable.empty()
|
||||
|
||||
val selectedConversation = selectedChips
|
||||
.skipWhile { it.isEmpty() }
|
||||
.map { chips -> chips.map { it.address } }
|
||||
.distinctUntilChanged()
|
||||
.doOnNext { newState { copy(loading = true) } }
|
||||
.observeOn(Schedulers.io())
|
||||
.map { addresses -> Pair(conversationRepo.getOrCreateConversation(addresses)?.id ?: 0, addresses) }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext { newState { copy(loading = false) } }
|
||||
.switchMap { (threadId, addresses) ->
|
||||
// If we already have this thread in realm, or we're able to obtain it from the
|
||||
// system, just return that.
|
||||
threadId.takeIf { it > 0 }?.let {
|
||||
return@switchMap conversationRepo.getConversationAsync(threadId).asObservable()
|
||||
}
|
||||
|
||||
// Otherwise, we'll monitor the conversations until our expected conversation is created
|
||||
conversationRepo.getConversations(prefs.unreadAtTop.get()).asObservable()
|
||||
.filter { it.isLoaded }
|
||||
.observeOn(Schedulers.io())
|
||||
.map { conversationRepo.getOrCreateConversation(addresses)?.id ?: 0 }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.switchMap { actualThreadId ->
|
||||
when (actualThreadId) {
|
||||
0L -> Observable.just(Conversation(0))
|
||||
else -> conversationRepo.getConversationAsync(actualThreadId).asObservable()
|
||||
}
|
||||
}
|
||||
.skipWhile { recipients -> recipients.isEmpty() }
|
||||
.map { recipients -> recipients.map { it.address } }
|
||||
.distinctUntilChanged()
|
||||
.doOnNext { newState { copy(loading = true) } }
|
||||
.observeOn(Schedulers.io()) // background thread for possible long telephony running
|
||||
.doOnNext { addresses -> conversationRepo.getOrCreateConversation(addresses) }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.switchMap { addresses ->
|
||||
// monitors convos and triggers when wanted convo is present
|
||||
conversationRepo.getConversations(false)
|
||||
.asObservable()
|
||||
.filter { conversations -> conversations.isLoaded }
|
||||
.mapNotNull { conversationRepo.getConversation(addresses) }
|
||||
.doOnNext { newState { copy(loading = false) } }
|
||||
.switchMap { conversation -> conversation.asObservable() }
|
||||
}
|
||||
|
||||
// Merges two potential conversation sources (threadId from constructor and contact selection) into a single
|
||||
// stream of conversations. If the conversation was deleted, notify the activity to shut down
|
||||
// Merges two potential conversation sources (constructor threadId and contact selection)
|
||||
// into a single stream of conversations. If the conversation was deleted, notify the
|
||||
// activity to shut down
|
||||
disposables += selectedConversation
|
||||
.mergeWith(initialConversation)
|
||||
.filter { conversation -> conversation.isLoaded }
|
||||
.doOnNext { conversation ->
|
||||
if (!conversation.isValid) {
|
||||
newState { copy(hasError = true) }
|
||||
}
|
||||
}
|
||||
.filter { conversation -> conversation.isValid }
|
||||
.subscribe(conversation::onNext)
|
||||
.mergeWith(initialConversation)
|
||||
.filter { it.isLoaded }
|
||||
.filter { it.isValid.also { if (!it) newState { copy(hasError = true) } } }
|
||||
.subscribe(conversation::onNext)
|
||||
|
||||
if (addresses.isNotEmpty()) {
|
||||
if (addresses.isNotEmpty())
|
||||
selectedChips.onNext(addresses.map { address -> Recipient(address = address) })
|
||||
}
|
||||
|
||||
disposables += chipsReducer
|
||||
.scan(listOf<Recipient>()) { previousState, reducer -> reducer(previousState) }
|
||||
@@ -1085,8 +1068,12 @@ class ComposeViewModel @Inject constructor(
|
||||
// Send a message when the send button is clicked, and disable editing mode if it's enabled
|
||||
view.sendIntent
|
||||
.observeOn(Schedulers.io())
|
||||
.withLatestFrom(view.textChangedIntent) { _, body -> body.toString() }
|
||||
.withLatestFrom(state, conversation, selectedChips) { body, state, conversation, chips ->
|
||||
.withLatestFrom(
|
||||
view.textChangedIntent,
|
||||
state,
|
||||
conversation,
|
||||
selectedChips
|
||||
) { _, body, state, conversation, chips ->
|
||||
if (!permissionManager.isDefaultSms()) {
|
||||
view.requestDefaultSms()
|
||||
return@withLatestFrom
|
||||
@@ -1115,60 +1102,49 @@ class ComposeViewModel @Inject constructor(
|
||||
false -> chips.map { chip -> chip.address }
|
||||
}
|
||||
val sendAsGroup = ((addresses.size > 1) && // if more than one address to send to
|
||||
(!state.editingMode || // and is not a new convo
|
||||
state.sendAsGroup)) // or (is a new convo and) send as group is selected
|
||||
(!state.editingMode || // and is not a new convo (group msg or not is already set)
|
||||
state.sendAsGroup)) // or (is a new convo and) send as group is selected
|
||||
|
||||
when {
|
||||
// Scheduling a message
|
||||
state.scheduled != 0L -> {
|
||||
state.scheduled != 0L -> addScheduledMessage.execute(
|
||||
AddScheduledMessage.Params(
|
||||
state.scheduled,
|
||||
subId,
|
||||
addresses,
|
||||
sendAsGroup,
|
||||
body.toString(),
|
||||
state.attachments.map { it.uri.toString() }
|
||||
)
|
||||
).also {
|
||||
newState { copy(scheduled = 0) }
|
||||
val uris = state.attachments.map { it.uri.toString() }
|
||||
val params = AddScheduledMessage
|
||||
.Params(state.scheduled, subId, addresses, sendAsGroup, body, uris)
|
||||
addScheduledMessage.execute(params)
|
||||
context.makeToast(R.string.compose_scheduled_toast)
|
||||
}
|
||||
|
||||
// Sending a group message
|
||||
sendAsGroup -> {
|
||||
sendMessage.execute(SendMessage
|
||||
.Params(subId, conversation.id, addresses, body, state.attachments, delay))
|
||||
}
|
||||
// sending a group message
|
||||
sendAsGroup -> sendMessage.execute(
|
||||
SendMessage.Params(
|
||||
subId,
|
||||
0,
|
||||
addresses,
|
||||
body.toString(),
|
||||
state.attachments,
|
||||
delay
|
||||
)
|
||||
)
|
||||
|
||||
// Sending a message to an existing conversation with one recipient
|
||||
conversation.recipients.size == 1 -> {
|
||||
val address = conversation.recipients.map { it.address }
|
||||
sendMessage.execute(SendMessage.Params(subId, threadId, address, body, state.attachments, delay))
|
||||
}
|
||||
|
||||
// Create a new conversation with one address
|
||||
addresses.size == 1 -> {
|
||||
sendMessage.execute(SendMessage
|
||||
.Params(subId, threadId, addresses, body, state.attachments, delay))
|
||||
}
|
||||
|
||||
// Send a message to multiple addresses
|
||||
else -> {
|
||||
addresses.forEach {
|
||||
val threadId = tryOrNull(false) {
|
||||
TelephonyCompat.getOrCreateThreadId(context, it)
|
||||
} ?: 0
|
||||
val address = conversationRepo.getOrCreateConversation(it)
|
||||
?.recipients
|
||||
?.firstOrNull()
|
||||
?.address
|
||||
?: it
|
||||
sendMessage.execute(
|
||||
SendMessage.Params(
|
||||
subId,
|
||||
threadId,
|
||||
listOf(address),
|
||||
body,
|
||||
state.attachments,
|
||||
delay
|
||||
)
|
||||
// sending message to individual address(es)
|
||||
else -> addresses.forEach {
|
||||
sendMessage.execute(
|
||||
SendMessage.Params(
|
||||
subId,
|
||||
0,
|
||||
listOf(it),
|
||||
body.toString(),
|
||||
state.attachments,
|
||||
delay
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1176,12 +1152,14 @@ class ComposeViewModel @Inject constructor(
|
||||
// compose activity)
|
||||
view.clearCurrentMessageIntent.onNext(
|
||||
((addresses.size > 1) && // if more than one address to send to
|
||||
state.editingMode && // and is a new convo
|
||||
!state.sendAsGroup) // and is *not* sent as a group
|
||||
)
|
||||
}
|
||||
.autoDisposable(view.scope())
|
||||
.subscribe{view.focusMessage()}
|
||||
state.editingMode && // and is a new convo
|
||||
!state.sendAsGroup) // and is *not* sent as a group
|
||||
)
|
||||
}
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext { view.focusMessage() }
|
||||
.autoDisposable(view.scope())
|
||||
.subscribe()
|
||||
|
||||
// View QKSMS+
|
||||
view.viewQksmsPlusIntent
|
||||
|
||||
Reference in New Issue
Block a user