#1010 - Message content search

This commit is contained in:
Moez Bhatti
2018-05-17 15:31:34 -04:00
parent 8374386734
commit e3307bf3dc
31 changed files with 540 additions and 91 deletions
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* This file is part of QKSMS.
*
* QKSMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QKSMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QKSMS. If not, see <http://www.gnu.org/licenses/>.
*/
package filter
import model.Contact
import util.extensions.removeAccents
import javax.inject.Inject
class ContactFilter @Inject constructor(private val phoneNumberFilter: PhoneNumberFilter) : Filter<Contact>() {
override fun filter(item: Contact, query: CharSequence): Boolean {
return item.name.removeAccents().contains(query, true) || // Name
item.numbers.map { it.address }.any { address -> phoneNumberFilter.filter(address, query) } // Number
}
}
@@ -0,0 +1,30 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* This file is part of QKSMS.
*
* QKSMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QKSMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QKSMS. If not, see <http://www.gnu.org/licenses/>.
*/
package filter
import model.Conversation
import javax.inject.Inject
class ConversationFilter @Inject constructor(private val recipientFilter: RecipientFilter) : Filter<Conversation>() {
override fun filter(item: Conversation, query: CharSequence): Boolean {
return item.recipients.any { recipient -> recipientFilter.filter(recipient, query) }
}
}
+25
View File
@@ -0,0 +1,25 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* This file is part of QKSMS.
*
* QKSMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QKSMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QKSMS. If not, see <http://www.gnu.org/licenses/>.
*/
package filter
abstract class Filter<in T> {
abstract fun filter(item: T, query: CharSequence): Boolean
}
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* This file is part of QKSMS.
*
* QKSMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QKSMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QKSMS. If not, see <http://www.gnu.org/licenses/>.
*/
package filter
import android.telephony.PhoneNumberUtils
import javax.inject.Inject
class PhoneNumberFilter @Inject constructor() : Filter<String>() {
override fun filter(item: String, query: CharSequence): Boolean {
val allCharactersDialable = query.all { PhoneNumberUtils.isReallyDialable(it) }
return allCharactersDialable && (PhoneNumberUtils.compare(item, query.toString()) ||
PhoneNumberUtils.stripSeparators(item).contains(PhoneNumberUtils.stripSeparators(query.toString())))
}
}
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* This file is part of QKSMS.
*
* QKSMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QKSMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QKSMS. If not, see <http://www.gnu.org/licenses/>.
*/
package filter
import model.Recipient
import javax.inject.Inject
class RecipientFilter @Inject constructor(
private val contactFilter: ContactFilter,
private val phoneNumberFilter: PhoneNumberFilter)
: Filter<Recipient>() {
override fun filter(item: Recipient, query: CharSequence) = when {
item.contact?.let { contactFilter.filter(it, query) } == true -> true
phoneNumberFilter.filter(item.address, query) -> true
else -> false
}
}
@@ -32,10 +32,11 @@ import android.telephony.PhoneNumberUtils
import android.telephony.SmsManager
import com.klinker.android.send_message.BroadcastUtils
import com.klinker.android.send_message.StripAccents
import io.reactivex.Flowable
import filter.ConversationFilter
import io.reactivex.Maybe
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import io.realm.Case
import io.realm.Realm
import io.realm.RealmResults
import io.realm.Sort
@@ -46,6 +47,7 @@ import model.Contact
import model.Conversation
import model.Message
import model.MmsPart
import model.SearchResult
import util.MessageUtils
import util.Preferences
import util.extensions.anyOf
@@ -59,13 +61,13 @@ 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 prefs: Preferences) : MessageRepository {
override fun getConversations(archived: Boolean): Flowable<List<Conversation>> {
val realm = Realm.getDefaultInstance()
return realm
override fun getConversations(archived: Boolean): RealmResults<Conversation> {
return Realm.getDefaultInstance()
.where(Conversation::class.java)
.notEqualTo("id", 0L)
.greaterThan("count", 0)
@@ -74,10 +76,6 @@ class MessageRepositoryImpl @Inject constructor(
.isNotEmpty("recipients")
.sort("date", Sort.DESCENDING)
.findAllAsync()
.asFlowable()
.filter { it.isLoaded }
.filter { it.isValid }
.map { conversations -> realm.copyFromRealm(conversations) }
}
override fun getConversationsSnapshot(): List<Conversation> {
@@ -92,6 +90,25 @@ class MessageRepositoryImpl @Inject constructor(
.findAll())
}
override fun searchConversations(query: String): List<SearchResult> {
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<Conversation> {
return Realm.getDefaultInstance()
.where(Conversation::class.java)
@@ -166,10 +183,11 @@ class MessageRepositoryImpl @Inject constructor(
}
}
override fun getMessages(threadId: Long): RealmResults<Message> {
override fun getMessages(threadId: Long, query: String): RealmResults<Message> {
return Realm.getDefaultInstance()
.where(Message::class.java)
.equalTo("threadId", threadId)
.let { if (query.isEmpty()) it else it.contains("body", query, Case.INSENSITIVE) }
.sort("date")
.findAllAsync()
}